Compare commits

..

4 Commits

Author SHA1 Message Date
Leonardo de Moura
0af36c5415 feat: simprocs for String and Char <, <=, >, >= 2024-05-20 13:50:30 -07:00
Leonardo de Moura
bd0f499108 feat: add instance for LE String 2024-05-20 13:49:04 -07:00
Leonardo de Moura
ce72435408 chore: fix test 2024-05-20 13:32:44 -07:00
Leonardo de Moura
a6636cff96 feat: some string simprocs 2024-05-20 13:10:45 -07:00
3576 changed files with 26390 additions and 143908 deletions

View File

@@ -25,7 +25,7 @@ Please put an X between the brackets as you perform the following steps:
### Context ### Context
[Broader context that the issue occurred in. If there was any prior discussion on [the Lean Zulip](https://leanprover.zulipchat.com), link it here as well.] [Broader context that the issue occured in. If there was any prior discussion on [the Lean Zulip](https://leanprover.zulipchat.com), link it here as well.]
### Steps to Reproduce ### Steps to Reproduce
@@ -39,7 +39,7 @@ Please put an X between the brackets as you perform the following steps:
### Versions ### Versions
[Output of `#version` or `#eval Lean.versionString`] [Output of `#eval Lean.versionString`]
[OS version, if not using live.lean-lang.org.] [OS version, if not using live.lean-lang.org.]
### Additional Information ### Additional Information

View File

@@ -5,17 +5,10 @@
* Include the link to your `RFC` or `bug` issue in the description. * 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. * If the issue does not already have approval from a developer, submit the PR as draft.
* The PR title/description will become the commit message. Keep it up-to-date as the PR evolves. * The PR title/description will become the commit message. Keep it up-to-date as the PR evolves.
* For `feat/fix` PRs, the first paragraph starting with "This PR" must be present and will become a
changelog entry unless the PR is labeled with `no-changelog`. If the PR does not have this label,
it must instead be categorized with one of the `changelog-*` labels (which will be done by a
reviewer for external PRs).
* A toolchain of the form `leanprover/lean4-pr-releases:pr-release-NNNN` for Linux and M-series Macs will be generated upon build. To generate binaries for Windows and Intel-based Macs as well, write a comment containing `release-ci` on its own line.
* If you rebase your PR onto `nightly-with-mathlib` then CI will test Mathlib against your PR. * 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. * 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. * Remove this section, up to and including the `---` before submitting.
--- ---
This PR <short changelog summary for feat/fix, see above>. Closes #0000 (`RFC` or `bug` issue number fixed by this PR, if any)
Closes <`RFC` or `bug` issue number fixed by this PR, if any>

View File

@@ -1,8 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
commit-message:
prefix: "chore: CI"

View File

@@ -15,8 +15,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: actionlint - name: actionlint
uses: raven-actions/actionlint@v2 uses: raven-actions/actionlint@v1
with: with:
pyflakes: false # we do not use python scripts pyflakes: false # we do not use python scripts

View File

@@ -11,10 +11,7 @@ jobs:
with: with:
# the default is to use a virtual merge commit between the PR and master: just use the PR # the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
sparse-checkout: | sparse-checkout: src/Lean
src/Lean
src/Std
src/lake/Lake
- name: Check Prelude - name: Check Prelude
run: | run: |
failed_files="" failed_files=""
@@ -22,8 +19,8 @@ jobs:
if ! grep -q "^prelude$" "$file"; then if ! grep -q "^prelude$" "$file"; then
failed_files="$failed_files$file\n" failed_files="$failed_files$file\n"
fi fi
done < <(find src/Lean src/Std src/lake/Lake -name '*.lean' -print0) done < <(find src/Lean -name '*.lean' -print0)
if [ -n "$failed_files" ]; then if [ -n "$failed_files" ]; then
echo -e "The following files should use 'prelude':\n$failed_files" echo -e "The following files should use 'prelude':\n$failed_files"
exit 1 exit 1
fi fi

View File

@@ -9,17 +9,6 @@ on:
merge_group: merge_group:
schedule: schedule:
- cron: '0 7 * * *' # 8AM CET/11PM PT - cron: '0 7 * * *' # 8AM CET/11PM PT
# for manual re-release of a nightly
workflow_dispatch:
inputs:
action:
description: 'Action'
required: true
default: 'release nightly'
type: choice
options:
- release nightly
concurrency: concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
@@ -31,10 +20,8 @@ jobs:
configure: configure:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
# 0: PRs without special label # Should we run only a quick CI? Yes on a pull request without the full-ci label
# 1: PRs with `merge-ci` label, merge queue checks, master commits quick: ${{ steps.set-quick.outputs.quick }}
# 2: PRs with `release-ci` label, releases (incl. nightlies)
check-level: ${{ steps.set-level.outputs.check-level }}
# The build matrix, dynamically generated here # The build matrix, dynamically generated here
matrix: ${{ steps.set-matrix.outputs.result }} matrix: ${{ steps.set-matrix.outputs.result }}
# Should we make a nightly release? If so, this output contains the lean version string, else it is empty # Should we make a nightly release? If so, this output contains the lean version string, else it is empty
@@ -51,12 +38,173 @@ jobs:
RELEASE_TAG: ${{ steps.set-release.outputs.RELEASE_TAG }} RELEASE_TAG: ${{ steps.set-release.outputs.RELEASE_TAG }}
steps: steps:
- name: Run quick CI?
id: set-quick
# We do not use github.event.pull_request.labels.*.name here because
# re-running a run does not update that list, and we do want to be able to
# rerun the workflow run after settings the `full-ci` label.
run: |
if [ "${{ github.event_name }}" == 'pull_request' ]
then
echo "quick=$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels | any(.name == "full-ci") | not')" >> "$GITHUB_OUTPUT"
else
echo "quick=false" >> "$GITHUB_OUTPUT"
fi
env:
GH_TOKEN: ${{ github.token }}
- name: Configure build matrix
id: set-matrix
uses: actions/github-script@v7
with:
script: |
const quick = ${{ steps.set-quick.outputs.quick }};
console.log(`quick: ${quick}`);
// use large runners outside PRs where available (original repo)
// disabled for now as this mostly just speeds up the test suite which is not a bottleneck
// let large = ${{ github.event_name != 'pull_request' && github.repository == 'leanprover/lean4' }} ? "-large" : "";
let matrix = [
{
// portable release build: use channel with older glibc (2.27)
"name": "Linux LLVM",
"os": "ubuntu-latest",
"release": false,
"quick": false,
"shell": "nix develop .#oldGlibc -c 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",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
// reverse-ffi needs to be updated to link to LLVM libraries
"CTEST_OPTIONS": "-E 'foreign|leanlaketest_reverse-ffi'",
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
},
{
"name": "Linux release",
"os": "ubuntu-latest",
"release": true,
"quick": true,
"shell": "nix develop .#oldGlibc -c 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",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'"
},
{
"name": "Linux",
"os": "ubuntu-latest",
"check-stage3": true,
"test-speedcenter": true,
"quick": false,
},
{
"name": "Linux Debug",
"os": "ubuntu-latest",
"quick": false,
"CMAKE_OPTIONS": "-DCMAKE_BUILD_TYPE=Debug",
// exclude seriously slow tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},
// TODO: suddenly started failing in CI
/*{
"name": "Linux fsanitize",
"os": "ubuntu-latest",
"quick": false,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_OPTIONS": "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address,undefined -DLEANC_EXTRA_FLAGS='-fsanitize=address,undefined -fsanitize-link-c++-runtime' -DSMALL_ALLOCATOR=OFF -DBSYMBOLIC=OFF",
// exclude seriously slow/problematic tests (laketests crash)
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},*/
{
"name": "macOS",
"os": "macos-13",
"release": true,
"quick": false,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
},
{
"name": "macOS aarch64",
"os": "macos-13",
"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": "../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
},
{
"name": "Windows",
"os": "windows-2022",
"release": true,
"quick": false,
"shell": "msys2 {0}",
"CMAKE_OPTIONS": "-G \"Unix Makefiles\" -DUSE_GMP=OFF",
// for reasons unknown, interactivetests are flaky on Windows
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd"
},
{
"name": "Linux aarch64",
"os": "ubuntu-latest",
"CMAKE_OPTIONS": "-DUSE_GMP=OFF -DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"quick": false,
"cross": true,
"cross_target": "aarch64-unknown-linux-gnu",
"shell": "nix develop .#oldGlibcAArch -c 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": "../script/prepare-llvm-linux.sh lean-llvm-aarch64-* lean-llvm-x86_64-*"
},
{
"name": "Linux 32bit",
"os": "ubuntu-latest",
// Use 32bit on stage0 and stage1 to keep oleans compatible
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86",
"cmultilib": true,
"release": true,
"quick": false,
"cross": true,
"shell": "bash -euxo pipefail {0}"
},
{
"name": "Web Assembly",
"os": "ubuntu-latest",
// Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32",
"wasm": true,
"cmultilib": true,
"release": true,
"quick": false,
"cross": true,
"shell": "bash -euxo pipefail {0}",
// Just a few selected tests because wasm is slow
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean\""
}
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
if (quick) {
return matrix.filter((job) => job.quick)
} else {
return matrix
}
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
# don't schedule nightlies on forks # don't schedule nightlies on forks
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly' if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
- name: Set Nightly - name: Set Nightly
if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4' || inputs.action == 'release nightly' if: github.event_name == 'schedule' && github.repository == 'leanprover/lean4'
id: set-nightly id: set-nightly
run: | run: |
if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then if [[ -n '${{ secrets.PUSH_NIGHTLY_TOKEN }}' ]]; then
@@ -101,168 +249,6 @@ jobs:
echo "Tag ${TAG_NAME} did not match SemVer regex." echo "Tag ${TAG_NAME} did not match SemVer regex."
fi fi
- name: Set check level
id: set-level
# We do not use github.event.pull_request.labels.*.name here because
# re-running a run does not update that list, and we do want to be able to
# rerun the workflow run after setting the `release-ci`/`merge-ci` labels.
run: |
check_level=0
if [[ -n "${{ steps.set-nightly.outputs.nightly }}" || -n "${{ steps.set-release.outputs.RELEASE_TAG }}" ]]; then
check_level=2
elif [[ "${{ github.event_name }}" != "pull_request" ]]; then
check_level=1
else
labels="$(gh api repos/${{ github.repository_owner }}/${{ github.event.repository.name }}/pulls/${{ github.event.pull_request.number }} --jq '.labels')"
if echo "$labels" | grep -q "release-ci"; then
check_level=2
elif echo "$labels" | grep -q "merge-ci"; then
check_level=1
fi
fi
echo "check-level=$check_level" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ github.token }}
- name: Configure build matrix
id: set-matrix
uses: actions/github-script@v7
with:
script: |
const level = ${{ steps.set-level.outputs.check-level }};
console.log(`level: ${level}`);
// use large runners where available (original repo)
let large = ${{ github.repository == 'leanprover/lean4' }};
let matrix = [
{
// portable release build: use channel with older glibc (2.27)
"name": "Linux LLVM",
"os": "ubuntu-latest",
"release": false,
"check-level": 2,
"shell": "nix develop .#oldGlibc -c 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",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
// reverse-ffi needs to be updated to link to LLVM libraries
"CTEST_OPTIONS": "-E 'foreign|leanlaketest_reverse-ffi'",
"CMAKE_OPTIONS": "-DLLVM=ON -DLLVM_CONFIG=${GITHUB_WORKSPACE}/build/llvm-host/bin/llvm-config"
},
{
"name": "Linux release",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"release": true,
"check-level": 0,
"shell": "nix develop .#oldGlibc -c 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",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*",
"binary-check": "ldd -v",
// foreign code may be linked against more recent glibc
"CTEST_OPTIONS": "-E 'foreign'"
},
{
"name": "Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-4x8" : "ubuntu-latest",
"check-stage3": level >= 2,
"test-speedcenter": level >= 2,
"check-level": 1,
},
{
"name": "Linux Debug",
"os": "ubuntu-latest",
"check-level": 2,
"CMAKE_PRESET": "debug",
// exclude seriously slow tests
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest|bv_bitblast_stress'"
},
// TODO: suddenly started failing in CI
/*{
"name": "Linux fsanitize",
"os": "ubuntu-latest",
"check-level": 2,
// turn off custom allocator & symbolic functions to make LSAN do its magic
"CMAKE_PRESET": "sanitize",
// exclude seriously slow/problematic tests (laketests crash)
"CTEST_OPTIONS": "-E 'interactivetest|leanpkgtest|laketest|benchtest'"
},*/
{
"name": "macOS",
"os": "macos-13",
"release": true,
"check-level": 2,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
},
{
"name": "macOS aarch64",
"os": "macos-14",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-darwin_aarch64",
"release": true,
"check-level": 0,
"shell": "bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-apple-darwin.tar.zst",
"prepare-llvm": "../script/prepare-llvm-macos.sh lean-llvm*",
"binary-check": "otool -L",
"tar": "gtar" // https://github.com/actions/runner-images/issues/2619
},
{
"name": "Windows",
"os": "windows-2022",
"release": true,
"check-level": 2,
"shell": "msys2 {0}",
"CMAKE_OPTIONS": "-G \"Unix Makefiles\"",
// for reasons unknown, interactivetests are flaky on Windows
"CTEST_OPTIONS": "--repeat until-pass:2",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-x86_64-w64-windows-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-mingw.sh lean-llvm*",
"binary-check": "ldd"
},
{
"name": "Linux aarch64",
"os": "nscloud-ubuntu-22.04-arm64-4x8",
"CMAKE_OPTIONS": "-DLEAN_INSTALL_SUFFIX=-linux_aarch64",
"release": true,
"check-level": 2,
"shell": "nix develop .#oldGlibcAArch -c bash -euxo pipefail {0}",
"llvm-url": "https://github.com/leanprover/lean-llvm/releases/download/15.0.1/lean-llvm-aarch64-linux-gnu.tar.zst",
"prepare-llvm": "../script/prepare-llvm-linux.sh lean-llvm*"
},
{
"name": "Linux 32bit",
"os": "ubuntu-latest",
// Use 32bit on stage0 and stage1 to keep oleans compatible
"CMAKE_OPTIONS": "-DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_MMAP=OFF -DUSE_GMP=OFF -DLEAN_EXTRA_CXX_FLAGS='-m32' -DLEANC_OPTS='-m32' -DMMAP=OFF -DLEAN_INSTALL_SUFFIX=-linux_x86 -DCMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/ -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/",
"cmultilib": true,
"release": true,
"check-level": 2,
"cross": true,
"shell": "bash -euxo pipefail {0}"
},
{
"name": "Web Assembly",
"os": "ubuntu-latest",
// Build a native 32bit binary in stage0 and use it to compile the oleans and the wasm build
"CMAKE_OPTIONS": "-DCMAKE_C_COMPILER_WORKS=1 -DSTAGE0_USE_GMP=OFF -DSTAGE0_LEAN_EXTRA_CXX_FLAGS='-m32' -DSTAGE0_LEANC_OPTS='-m32' -DSTAGE0_CMAKE_CXX_COMPILER=clang++ -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_EXECUTABLE_SUFFIX=\"\" -DUSE_GMP=OFF -DMMAP=OFF -DSTAGE0_MMAP=OFF -DCMAKE_AR=../emsdk/emsdk-main/upstream/emscripten/emar -DCMAKE_TOOLCHAIN_FILE=../emsdk/emsdk-main/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake -DLEAN_INSTALL_SUFFIX=-linux_wasm32 -DSTAGE0_CMAKE_LIBRARY_PATH=/usr/lib/i386-linux-gnu/",
"wasm": true,
"cmultilib": true,
"release": true,
"check-level": 2,
"cross": true,
"shell": "bash -euxo pipefail {0}",
// Just a few selected tests because wasm is slow
"CTEST_OPTIONS": "-R \"leantest_1007\\.lean|leantest_Format\\.lean|leanruntest\\_1037.lean|leanruntest_ac_rfl\\.lean|leanruntest_tempfile.lean\\.|leanruntest_libuv\\.lean\""
}
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`)
return matrix.filter((job) => level >= job["check-level"])
build: build:
needs: [configure] needs: [configure]
if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4' if: github.event_name != 'schedule' || github.repository == 'leanprover/lean4'
@@ -289,67 +275,63 @@ jobs:
CXX: c++ CXX: c++
MACOSX_DEPLOYMENT_TARGET: 10.15 MACOSX_DEPLOYMENT_TARGET: 10.15
steps: steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
- name: Install Nix - name: Install Nix
uses: DeterminateSystems/nix-installer-action@main uses: cachix/install-nix-action@v18
with:
install_url: https://releases.nixos.org/nix/nix-2.12.0/install
if: runner.os == 'Linux' && !matrix.cmultilib if: runner.os == 'Linux' && !matrix.cmultilib
- name: Install MSYS2 - name: Install MSYS2
uses: msys2/setup-msys2@v2 uses: msys2/setup-msys2@v2
with: with:
msystem: clang64 msystem: clang64
# `:` means do not prefix with msystem # `:p` means prefix with appropriate msystem prefix
pacboy: "make: python: cmake clang ccache gmp libuv git: zip: unzip: diffutils: binutils: tree: zstd tar:" pacboy: "make python cmake:p clang:p ccache:p gmp:p git zip unzip diffutils binutils tree zstd:p tar"
if: runner.os == 'Windows' if: runner.os == 'Windows'
- name: Install Brew Packages - name: Install Brew Packages
run: | run: |
brew install ccache tree zstd coreutils gmp libuv brew install ccache tree zstd coreutils gmp
if: runner.os == 'macOS' if: runner.os == 'macOS'
- name: Checkout
uses: actions/checkout@v4
with:
# the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }}
# Do check out some CI-relevant files from virtual merge commit to accommodate CI changes on
# master (as the workflow files themselves are always taken from the merge)
# (needs to be after "Install *" to use the right shell)
- name: CI Merge Checkout
run: |
git fetch --depth=1 origin ${{ github.sha }}
git checkout FETCH_HEAD flake.nix flake.lock
if: github.event_name == 'pull_request'
# (needs to be after "Checkout" so files don't get overridden)
- name: Setup emsdk - name: Setup emsdk
uses: mymindstorm/setup-emsdk@v14 uses: mymindstorm/setup-emsdk@v12
with: with:
version: 3.1.44 version: 3.1.44
actions-cache-folder: emsdk actions-cache-folder: emsdk
if: matrix.wasm if: matrix.wasm
- name: Install 32bit c libs - name: Install 32bit c libs
run: | run: |
sudo dpkg --add-architecture i386
sudo apt-get update sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib ccache libuv1-dev:i386 sudo apt-get install -y gcc-multilib g++-multilib ccache
if: matrix.cmultilib if: matrix.cmultilib
- name: Cache - name: Cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: .ccache path: .ccache
key: ${{ matrix.name }}-build-v3-${{ github.event.pull_request.head.sha }} key: ${{ matrix.name }}-build-v3-${{ github.sha }}
# fall back to (latest) previous cache # fall back to (latest) previous cache
restore-keys: | restore-keys: |
${{ matrix.name }}-build-v3 ${{ matrix.name }}-build-v3
save-always: true
# open nix-shell once for initial setup
- name: Setup - name: Setup
run: | run: |
ccache --zero-stats # open nix-shell once for initial setup
true
if: runner.os == 'Linux' if: runner.os == 'Linux'
- name: Set up NPROC - name: Set up core dumps
run: | run: |
echo "NPROC=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)" >> $GITHUB_ENV mkdir -p $PWD/coredumps
# store in current directory, for easy uploading together with binary
echo $PWD/coredumps/%e.%p.%t | sudo tee /proc/sys/kernel/core_pattern
if: runner.os == 'Linux'
- name: Build - name: Build
run: | run: |
mkdir build mkdir build
cd build cd build
ulimit -c unlimited # coredumps
# arguments passed to `cmake` # arguments passed to `cmake`
# this also enables githash embedding into stage 1 library # this also enables githash embedding into stage 1 library
OPTIONS=(-DCHECK_OLEAN_VERSION=ON) OPTIONS=(-DCHECK_OLEAN_VERSION=ON)
@@ -375,19 +357,11 @@ jobs:
OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }}) OPTIONS+=(-DLEAN_SPECIAL_VERSION_DESC=${{ needs.configure.outputs.LEAN_SPECIAL_VERSION_DESC }})
fi fi
# contortion to support empty OPTIONS with old macOS bash # contortion to support empty OPTIONS with old macOS bash
cmake .. --preset ${{ matrix.CMAKE_PRESET || 'release' }} -B . ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/.. cmake .. ${{ matrix.CMAKE_OPTIONS }} ${OPTIONS[@]+"${OPTIONS[@]}"} -DLEAN_INSTALL_PREFIX=$PWD/..
time make -j$NPROC make -j4
- name: Install make install
run: |
make -C build install
- name: Check Binaries - name: Check Binaries
run: ${{ matrix.binary-check }} lean-*/bin/* || true run: ${{ matrix.binary-check }} lean-*/bin/* || true
- name: Count binary symbols
run: |
for f in lean-*/bin/*; do
echo "$f: $(nm $f | grep " T " | wc -l) exported symbols"
done
if: matrix.name == 'Windows'
- name: List Install Tree - name: List Install Tree
run: | run: |
# omit contents of Init/, ... # omit contents of Init/, ...
@@ -403,7 +377,7 @@ jobs:
else else
${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst ${{ matrix.tar || 'tar' }} cf - $dir | zstd -T0 --no-progress -o pack/$dir.tar.zst
fi fi
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
if: matrix.release if: matrix.release
with: with:
name: build-${{ matrix.name }} name: build-${{ matrix.name }}
@@ -413,46 +387,74 @@ jobs:
build/stage1/bin/lean --stats src/Lean.lean build/stage1/bin/lean --stats src/Lean.lean
if: ${{ !matrix.cross }} if: ${{ !matrix.cross }}
- name: Test - name: Test
id: test
run: | run: |
time ctest --preset ${{ matrix.CMAKE_PRESET || 'release' }} --test-dir build/stage1 -j$NPROC --output-junit test-results.xml ${{ matrix.CTEST_OPTIONS }} cd build/stage1
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.check-level >= 1 ulimit -c unlimited # coredumps
# exclude nonreproducible test
ctest -j4 --progress --output-junit test-results.xml --output-on-failure ${{ matrix.CTEST_OPTIONS }} < /dev/null
if: (matrix.wasm || !matrix.cross) && needs.configure.outputs.quick == 'false'
- name: Test Summary - name: Test Summary
uses: test-summary/action@v2 uses: test-summary/action@v2
with: with:
paths: build/stage1/test-results.xml paths: build/stage1/test-results.xml
# prefix `if` above with `always` so it's run even if tests failed # prefix `if` above with `always` so it's run even if tests failed
if: always() && steps.test.conclusion != 'skipped' if: always() && (matrix.wasm || !matrix.cross) && needs.configure.outputs.quick == 'false'
- name: Check Test Binary - name: Check Test Binary
run: ${{ matrix.binary-check }} tests/compiler/534.lean.out run: ${{ matrix.binary-check }} tests/compiler/534.lean.out
if: (!matrix.cross) && steps.test.conclusion != 'skipped' if: ${{ !matrix.cross && needs.configure.outputs.quick == 'false' }}
- name: Build Stage 2 - name: Build Stage 2
run: | run: |
make -C build -j$NPROC stage2 cd build
ulimit -c unlimited # coredumps
make -j4 stage2
if: matrix.test-speedcenter if: matrix.test-speedcenter
- name: Check Stage 3 - name: Check Stage 3
run: | run: |
make -C build -j$NPROC check-stage3 cd build
ulimit -c unlimited # coredumps
make -j4 check-stage3
if: matrix.test-speedcenter if: matrix.test-speedcenter
- name: Test Speedcenter Benchmarks - name: Test Speedcenter Benchmarks
run: | run: |
# Necessary for some timing metrics but does not work on Namespace runners echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
# and we just want to test that the benchmarks run at all here
#echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH export BUILD=$PWD/build PATH=$PWD/build/stage1/bin:$PATH
cd tests/bench cd tests/bench
nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1 nix shell .#temci -c temci exec --config speedcenter.yaml --included_blocks fast --runs 1
if: matrix.test-speedcenter if: matrix.test-speedcenter
- name: Check rebootstrap - name: Check rebootstrap
run: | run: |
cd build
ulimit -c unlimited # coredumps
# clean rebuild in case of Makefile changes # clean rebuild in case of Makefile changes
make -C build update-stage0 && rm -rf build/stage* && make -C build -j$NPROC make update-stage0 && rm -rf ./stage* && make -j4
if: matrix.name == 'Linux' && needs.configure.outputs.check-level >= 1 if: matrix.name == 'Linux' && needs.configure.outputs.quick == 'false'
- name: CCache stats - name: CCache stats
run: ccache -s run: ccache -s
- name: Show stacktrace for coredumps
if: ${{ failure() && runner.os == 'Linux' }}
run: |
for c in coredumps/*; do
progbin="$(file $c | sed "s/.*execfn: '\([^']*\)'.*/\1/")"
echo bt | $GDB/bin/gdb -q $progbin $c || true
done
# has not been used in a long while, would need to be adapted to new
# shared libs
#- name: Upload coredumps
# uses: actions/upload-artifact@v3
# if: ${{ failure() && runner.os == 'Linux' }}
# with:
# name: coredumps-${{ matrix.name }}
# path: |
# ./coredumps
# ./build/stage0/bin/lean
# ./build/stage0/lib/lean/libleanshared.so
# ./build/stage1/bin/lean
# ./build/stage1/lib/lean/libleanshared.so
# ./build/stage2/bin/lean
# ./build/stage2/lib/lean/libleanshared.so
# This job collects results from all the matrix jobs # This job collects results from all the matrix jobs
# This can be made the "required" job, instead of listing each # This can be made the required job, instead of listing each
# matrix job separately # matrix job separately
all-done: all-done:
name: Build matrix complete name: Build matrix complete
@@ -461,24 +463,12 @@ jobs:
# mark as merely cancelled not failed if builds are cancelled # mark as merely cancelled not failed if builds are cancelled
if: ${{ !cancelled() }} if: ${{ !cancelled() }}
steps: steps:
- if: ${{ contains(needs.*.result, 'failure') && github.repository == 'leanprover/lean4' && github.ref_name == 'master' }}
uses: zulip/github-actions-zulip/send-message@v1
with:
api-key: ${{ secrets.ZULIP_BOT_KEY }}
email: "github-actions-bot@lean-fro.zulipchat.com"
organization-url: "https://lean-fro.zulipchat.com"
to: "infrastructure"
topic: "Github actions"
type: "stream"
content: |
A build of `${{ github.ref_name }}`, triggered by event `${{ github.event_name }}`, [failed](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}).
- if: contains(needs.*.result, 'failure') - if: contains(needs.*.result, 'failure')
uses: actions/github-script@v7 uses: actions/github-script@v7
with: with:
script: | script: |
core.setFailed('Some jobs failed') core.setFailed('Some jobs failed')
# This job creates releases from tags # This job creates releases from tags
# (whether they are "unofficial" releases for experiments, or official releases when the tag is "v" followed by a semver string.) # (whether they are "unofficial" releases for experiments, or official releases when the tag is "v" followed by a semver string.)
# We do not attempt to automatically construct a changelog here: # We do not attempt to automatically construct a changelog here:
@@ -488,22 +478,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: build needs: build
steps: steps:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
path: artifacts path: artifacts
- name: Release - name: Release
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
with: with:
files: artifacts/*/* files: artifacts/*/*
fail_on_unmatched_files: true fail_on_unmatched_files: true
prerelease: ${{ !startsWith(github.ref, 'refs/tags/v') || contains(github.ref, '-rc') }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update release.lean-lang.org
run: |
gh workflow -R leanprover/release-index run update-index.yml
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}
# This job creates nightly releases during the cron job. # This job creates nightly releases during the cron job.
# It is responsible for creating the tag, and automatically generating a changelog. # It is responsible for creating the tag, and automatically generating a changelog.
@@ -513,12 +497,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
# needed for tagging # needed for tagging
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.PUSH_NIGHTLY_TOKEN }} token: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v3
with: with:
path: artifacts path: artifacts
- name: Prepare Nightly Release - name: Prepare Nightly Release
@@ -536,7 +520,7 @@ jobs:
echo -e "\n*Full commit log*\n" >> diff.md 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 - name: Release Nightly
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
with: with:
body_path: diff.md body_path: diff.md
prerelease: true prerelease: true
@@ -546,13 +530,3 @@ jobs:
repository: ${{ github.repository_owner }}/lean4-nightly repository: ${{ github.repository_owner }}/lean4-nightly
env: env:
GITHUB_TOKEN: ${{ secrets.PUSH_NIGHTLY_TOKEN }} GITHUB_TOKEN: ${{ secrets.PUSH_NIGHTLY_TOKEN }}
- name: Update release.lean-lang.org
run: |
gh workflow -R leanprover/release-index run update-index.yml
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_INDEX_TOKEN }}
- name: Update toolchain on mathlib4's nightly-testing branch
run: |
gh workflow -R leanprover-community/mathlib4 run nightly_bump_toolchain.yml
env:
GITHUB_TOKEN: ${{ secrets.MATHLIB4_BOT }}

View File

@@ -1,34 +0,0 @@
name: Jira sync
on:
issues:
types: [closed]
jobs:
jira-sync:
runs-on: ubuntu-latest
steps:
- name: Move Jira issue to Done
env:
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
JIRA_USERNAME: ${{ secrets.JIRA_USERNAME }}
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
run: |
issue_number=${{ github.event.issue.number }}
jira_issue_key=$(curl -s -u "${JIRA_USERNAME}:${JIRA_API_TOKEN}" \
-X GET -H "Content-Type: application/json" \
"${JIRA_BASE_URL}/rest/api/2/search?jql=summary~\"${issue_number}\"" | \
jq -r '.issues[0].key')
if [ -z "$jira_issue_key" ]; then
exit
fi
curl -s -u "${JIRA_USERNAME}:${JIRA_API_TOKEN}" \
-X POST -H "Content-Type: application/json" \
--data "{\"transition\": {\"id\": \"41\"}}" \
"${JIRA_BASE_URL}/rest/api/2/issue/${jira_issue_key}/transitions"
echo "Moved Jira issue ${jira_issue_key} to Done"

View File

@@ -1,8 +1,6 @@
# This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, `WIP`, # This workflow allows any user to add one of the `awaiting-review`, `awaiting-author`, or `WIP` labels,
# `release-ci`, or a `changelog-XXX` label by commenting on the PR or issue. # by commenting on the PR or issue.
# If any labels from the set {`awaiting-review`, `awaiting-author`, `WIP`} are added, other labels # Other labels from this set are removed automatically at the same time.
# from that set are removed automatically at the same time.
# Similarly, if any `changelog-XXX` label is added, other `changelog-YYY` labels are removed.
name: Label PR based on Comment name: Label PR based on Comment
@@ -12,7 +10,7 @@ on:
jobs: jobs:
update-label: update-label:
if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP') || contains(github.event.comment.body, 'release-ci') || contains(github.event.comment.body, 'changelog-')) if: github.event.issue.pull_request != null && (contains(github.event.comment.body, 'awaiting-review') || contains(github.event.comment.body, 'awaiting-author') || contains(github.event.comment.body, 'WIP'))
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -21,14 +19,12 @@ jobs:
with: with:
github-token: ${{ secrets.GITHUB_TOKEN }} github-token: ${{ secrets.GITHUB_TOKEN }}
script: | script: |
const { owner, repo, number: issue_number } = context.issue; const { owner, repo, number: issue_number } = context.issue;
const commentLines = context.payload.comment.body.split('\r\n'); const commentLines = context.payload.comment.body.split('\r\n');
const awaitingReview = commentLines.includes('awaiting-review'); const awaitingReview = commentLines.includes('awaiting-review');
const awaitingAuthor = commentLines.includes('awaiting-author'); const awaitingAuthor = commentLines.includes('awaiting-author');
const wip = commentLines.includes('WIP'); const wip = commentLines.includes('WIP');
const releaseCI = commentLines.includes('release-ci');
const changelogMatch = commentLines.find(line => line.startsWith('changelog-'));
if (awaitingReview || awaitingAuthor || wip) { if (awaitingReview || awaitingAuthor || wip) {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-review' }).catch(() => {}); await github.rest.issues.removeLabel({ owner, repo, issue_number, name: 'awaiting-review' }).catch(() => {});
@@ -45,23 +41,3 @@ jobs:
if (wip) { if (wip) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['WIP'] }); await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['WIP'] });
} }
if (releaseCI) {
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: ['release-ci'] });
}
if (changelogMatch) {
const changelogLabel = changelogMatch.trim();
const { data: existingLabels } = await github.rest.issues.listLabelsOnIssue({ owner, repo, issue_number });
const changelogLabels = existingLabels.filter(label => label.name.startsWith('changelog-'));
// Remove all other changelog labels
for (const label of changelogLabels) {
if (label.name !== changelogLabel) {
await github.rest.issues.removeLabel({ owner, repo, issue_number, name: label.name }).catch(() => {});
}
}
// Add the new changelog label
await github.rest.issues.addLabels({ owner, repo, issue_number, labels: [changelogLabel] });
}

View File

@@ -13,36 +13,18 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
# see ci.yml
configure:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.result }}
steps:
- name: Configure build matrix
id: set-matrix
uses: actions/github-script@v7
with:
script: |
let large = ${{ github.repository == 'leanprover/lean4' }};
let matrix = [
{
"name": "Nix Linux",
"os": large ? "nscloud-ubuntu-22.04-amd64-8x8" : "ubuntu-latest",
}
];
console.log(`matrix:\n${JSON.stringify(matrix, null, 2)}`);
return matrix;
Build: Build:
needs: [configure]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
defaults: defaults:
run: run:
shell: nix run .#ciShell -- bash -euxo pipefail {0} shell: nix run .#ciShell -- bash -euxo pipefail {0}
strategy: strategy:
matrix: matrix:
include: ${{fromJson(needs.configure.outputs.matrix)}} include:
- name: Nix Linux
os: ubuntu-latest
#- name: Nix macOS
# os: macos-latest
# complete all jobs # complete all jobs
fail-fast: false fail-fast: false
name: ${{ matrix.name }} name: ${{ matrix.name }}
@@ -50,19 +32,18 @@ jobs:
NIX_BUILD_ARGS: --print-build-logs --fallback NIX_BUILD_ARGS: --print-build-logs --fallback
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
# the default is to use a virtual merge commit between the PR and master: just use the PR # the default is to use a virtual merge commit between the PR and master: just use the PR
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Set Up Nix Cache - name: Set Up Nix Cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: nix-store-cache path: nix-store-cache
key: ${{ matrix.name }}-nix-store-cache-${{ github.sha }} key: ${{ matrix.name }}-nix-store-cache-${{ github.sha }}
# fall back to (latest) previous cache # fall back to (latest) previous cache
restore-keys: | restore-keys: |
${{ matrix.name }}-nix-store-cache ${{ matrix.name }}-nix-store-cache
save-always: true
- name: Further Set Up Nix Cache - name: Further Set Up Nix Cache
shell: bash -euxo pipefail {0} shell: bash -euxo pipefail {0}
run: | run: |
@@ -79,14 +60,13 @@ jobs:
sudo mkdir -m0770 -p /nix/var/cache/ccache sudo mkdir -m0770 -p /nix/var/cache/ccache
sudo chown -R $USER /nix/var/cache/ccache sudo chown -R $USER /nix/var/cache/ccache
- name: Setup CCache Cache - name: Setup CCache Cache
uses: actions/cache@v4 uses: actions/cache@v3
with: with:
path: /nix/var/cache/ccache path: /nix/var/cache/ccache
key: ${{ matrix.name }}-nix-ccache-${{ github.sha }} key: ${{ matrix.name }}-nix-ccache-${{ github.sha }}
# fall back to (latest) previous cache # fall back to (latest) previous cache
restore-keys: | restore-keys: |
${{ matrix.name }}-nix-ccache ${{ matrix.name }}-nix-ccache
save-always: true
- name: Further Set Up CCache Cache - name: Further Set Up CCache Cache
run: | run: |
sudo chown -R root:nixbld /nix/var/cache sudo chown -R root:nixbld /nix/var/cache
@@ -96,7 +76,7 @@ jobs:
nix build $NIX_BUILD_ARGS .#cacheRoots -o push-build nix build $NIX_BUILD_ARGS .#cacheRoots -o push-build
- name: Test - name: Test
run: | run: |
nix build --keep-failed $NIX_BUILD_ARGS .#test -o push-test || (ln -s /tmp/nix-build-*/build/source/src/build ./push-test; false) nix build --keep-failed $NIX_BUILD_ARGS .#test -o push-test || (ln -s /tmp/nix-build-*/source/src/build/ ./push-test; false)
- name: Test Summary - name: Test Summary
uses: test-summary/action@v2 uses: test-summary/action@v2
with: with:
@@ -105,11 +85,19 @@ jobs:
continue-on-error: true continue-on-error: true
- name: Build manual - name: Build manual
run: | run: |
nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc#{lean-mdbook,leanInk,alectryon,inked} -o push-doc 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 nix build $NIX_BUILD_ARGS --update-input lean --no-write-lock-file ./doc
# https://github.com/netlify/cli/issues/1809 # https://github.com/netlify/cli/issues/1809
cp -r --dereference ./result ./dist cp -r --dereference ./result ./dist
if: matrix.name == 'Nix Linux' 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: Rebuild Nix Store Cache - name: Rebuild Nix Store Cache
run: | run: |
rm -rf nix-store-cache || true rm -rf nix-store-cache || true
@@ -121,7 +109,7 @@ jobs:
python3 -c 'import base64; print("alias="+base64.urlsafe_b64encode(bytes.fromhex("${{github.sha}}")).decode("utf-8").rstrip("="))' >> "$GITHUB_OUTPUT" 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" echo "message=`git log -1 --pretty=format:"%s"`" >> "$GITHUB_OUTPUT"
- name: Publish manual to Netlify - name: Publish manual to Netlify
uses: nwtgck/actions-netlify@v3.0 uses: nwtgck/actions-netlify@v2.0
id: publish-manual id: publish-manual
with: with:
publish-dir: ./dist publish-dir: ./dist
@@ -140,3 +128,5 @@ jobs:
- name: Fixup CCache Cache - name: Fixup CCache Cache
run: | run: |
sudo chown -R $USER /nix/var/cache sudo chown -R $USER /nix/var/cache
- name: CCache stats
run: CCACHE_DIR=/nix/var/cache/ccache nix run .#nixpkgs.ccache -- -s

View File

@@ -1,25 +0,0 @@
name: Check PR body for changelog convention
on:
merge_group:
pull_request:
types: [opened, synchronize, reopened, edited, labeled, converted_to_draft, ready_for_review]
jobs:
check-pr-body:
runs-on: ubuntu-latest
steps:
- name: Check PR body
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const { title, body, labels, draft } = context.payload.pull_request;
if (!draft && /^(feat|fix):/.test(title) && !labels.some(label => label.name == "changelog-no")) {
if (!labels.some(label => label.name.startsWith("changelog-"))) {
core.setFailed('feat/fix PR must have a `changelog-*` label');
}
if (!/^This PR [^<]/.test(body)) {
core.setFailed('feat/fix PR must have changelog summary starting with "This PR ..." as first line.');
}
}

View File

@@ -34,7 +34,7 @@ jobs:
- name: Download artifact from the previous workflow. - name: Download artifact from the previous workflow.
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }} if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
id: download-artifact id: download-artifact
uses: dawidd6/action-download-artifact@v7 # https://github.com/marketplace/actions/download-workflow-artifact uses: dawidd6/action-download-artifact@v2 # https://github.com/marketplace/actions/download-workflow-artifact
with: with:
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
path: artifacts path: artifacts
@@ -60,7 +60,7 @@ jobs:
GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }} GH_TOKEN: ${{ secrets.PR_RELEASES_TOKEN }}
- name: Release - name: Release
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }} if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: softprops/action-gh-release@v2 uses: softprops/action-gh-release@v1
with: with:
name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }} name: Release for PR ${{ steps.workflow-info.outputs.pullRequestNumber }}
# There are coredumps files here as well, but all in deeper subdirectories. # There are coredumps files here as well, but all in deeper subdirectories.
@@ -75,7 +75,7 @@ jobs:
- name: Report release status - name: Report release status
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }} if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: actions/github-script@v7 uses: actions/github-script@v6
with: with:
script: | script: |
await github.rest.repos.createCommitStatus({ await github.rest.repos.createCommitStatus({
@@ -111,7 +111,7 @@ jobs:
- name: 'Setup jq' - name: 'Setup jq'
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }} if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' }}
uses: dcarbone/install-jq-action@v3.0.1 uses: dcarbone/install-jq-action@v1.0.1
# Check that the most recently nightly coincides with 'git merge-base HEAD master' # Check that the most recently nightly coincides with 'git merge-base HEAD master'
- name: Check merge-base and nightly-testing-YYYY-MM-DD - name: Check merge-base and nightly-testing-YYYY-MM-DD
@@ -134,7 +134,7 @@ jobs:
MESSAGE="" MESSAGE=""
if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then if [[ -n "$MATHLIB_REMOTE_TAGS" ]]; then
echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag." echo "... and Mathlib has a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag."
else else
echo "... but Mathlib does not yet have a 'nightly-testing-$MOST_RECENT_NIGHTLY' tag." 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." 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."
@@ -149,7 +149,7 @@ jobs:
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA" echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
git -C lean4.git log -10 origin/master git -C lean4.git log -10 origin/master
git -C lean4.git fetch origin nightly-with-mathlib git -C lean4.git fetch origin nightly-with-mathlib
NIGHTLY_WITH_MATHLIB_SHA="$(git -C lean4.git rev-parse "origin/nightly-with-mathlib")" NIGHTLY_WITH_MATHLIB_SHA="$(git -C lean4.git rev-parse "origin/nightly-with-mathlib")"
MESSAGE="- ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_WITH_MATHLIB_SHA\`." MESSAGE="- ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_WITH_MATHLIB_SHA\`."
fi fi
@@ -163,11 +163,10 @@ jobs:
# so keep in sync # so keep in sync
# Use GitHub API to check if a comment already exists # Use GitHub API to check if a comment already exists
existing_comment="$(curl --retry 3 --location --silent \ existing_comment="$(curl -L -s -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \ "https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" \
| jq 'first(.[] | select(.body | test("^- . Mathlib") or startswith("Mathlib CI status")) | select(.user.login == "leanprover-community-bot"))')" | 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_id="$(echo "$existing_comment" | jq -r .id)"
existing_comment_body="$(echo "$existing_comment" | jq -r .body)" existing_comment_body="$(echo "$existing_comment" | jq -r .body)"
@@ -177,14 +176,14 @@ jobs:
echo "Posting message to the comments: $MESSAGE" echo "Posting message to the comments: $MESSAGE"
# Append new result to the existing comment or post a new comment # Append new result to the existing comment or post a new comment
# It's essential we use the MATHLIB4_COMMENT_BOT token here, so that Mathlib CI can subsequently edit the 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 if [ -z "$existing_comment_id" ]; then
INTRO="Mathlib CI status ([docs](https://leanprover-community.github.io/contribute/tags_and_branches.html)):" INTRO="Mathlib CI status ([docs](https://leanprover-community.github.io/contribute/tags_and_branches.html)):"
# Post new comment with a bullet point # Post new comment with a bullet point
echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" echo "Posting as new comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
curl -L -s \ curl -L -s \
-X POST \ -X POST \
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \ -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
-d "$(jq --null-input --arg intro "$INTRO" --arg val "$MESSAGE" '{"body":($intro + "\n" + $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" "https://api.github.com/repos/leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
@@ -193,7 +192,7 @@ jobs:
echo "Appending to existing comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments" echo "Appending to existing comment at leanprover/lean4/issues/${{ steps.workflow-info.outputs.pullRequestNumber }}/comments"
curl -L -s \ curl -L -s \
-X PATCH \ -X PATCH \
-H "Authorization: token ${{ secrets.MATHLIB4_COMMENT_BOT }}" \ -H "Authorization: token ${{ secrets.MATHLIB4_BOT }}" \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
-d "$(jq --null-input --arg existing "$existing_comment_body" --arg message "$MESSAGE" '{"body":($existing + "\n" + $message)}')" \ -d "$(jq --null-input --arg existing "$existing_comment_body" --arg message "$MESSAGE" '{"body":($existing + "\n" + $message)}')" \
"https://api.github.com/repos/leanprover/lean4/issues/comments/$existing_comment_id" "https://api.github.com/repos/leanprover/lean4/issues/comments/$existing_comment_id"
@@ -208,7 +207,7 @@ jobs:
- name: Report mathlib base - name: Report mathlib base
if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' }} if: ${{ steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' }}
uses: actions/github-script@v7 uses: actions/github-script@v6
with: with:
script: | script: |
const description = const description =
@@ -235,7 +234,7 @@ jobs:
# Checkout the Batteries repository with all branches # Checkout the Batteries repository with all branches
- name: Checkout Batteries repository - name: Checkout Batteries repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
repository: leanprover-community/batteries repository: leanprover-community/batteries
token: ${{ secrets.MATHLIB4_BOT }} token: ${{ secrets.MATHLIB4_BOT }}
@@ -292,20 +291,13 @@ jobs:
# Checkout the mathlib4 repository with all branches # Checkout the mathlib4 repository with all branches
- name: Checkout mathlib4 repository - name: Checkout mathlib4 repository
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
repository: leanprover-community/mathlib4 repository: leanprover-community/mathlib4
token: ${{ secrets.MATHLIB4_BOT }} token: ${{ secrets.MATHLIB4_BOT }}
ref: nightly-testing ref: nightly-testing
fetch-depth: 0 # This ensures we check out all tags and branches. fetch-depth: 0 # This ensures we check out all tags and branches.
- name: install elan
run: |
set -o pipefail
curl -sSfL https://github.com/leanprover/elan/releases/download/v3.0.0/elan-x86_64-unknown-linux-gnu.tar.gz | tar xz
./elan-init -y --default-toolchain none
echo "$HOME/.elan/bin" >> "${GITHUB_PATH}"
- name: Check if tag exists - name: Check if tag exists
if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true' if: steps.workflow-info.outputs.pullRequestNumber != '' && steps.ready.outputs.mathlib_ready == 'true'
id: check_mathlib_tag id: check_mathlib_tag
@@ -329,18 +321,15 @@ jobs:
git switch -c lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} "$BASE" 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 echo "leanprover/lean4-pr-releases:pr-release-${{ steps.workflow-info.outputs.pullRequestNumber }}" > lean-toolchain
git add lean-toolchain git add lean-toolchain
sed -i 's,require "leanprover-community" / "batteries" @ git ".\+",require "leanprover-community" / "batteries" @ git "lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }}",' lakefile.lean sed -i "s/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \".\+\"/require batteries from git \"https:\/\/github.com\/leanprover-community\/batteries\" @ \"nightly-testing-${MOST_RECENT_NIGHTLY}\"/" lakefile.lean
lake update batteries git add lakefile.lean
git add lakefile.lean lake-manifest.json
git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}" git commit -m "Update lean-toolchain for testing https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
else else
echo "Branch already exists, merging $BASE and bumping Batteries." echo "Branch already exists, pushing an empty commit."
git switch lean-pr-testing-${{ steps.workflow-info.outputs.pullRequestNumber }} 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. # 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.) # (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 merge "$BASE" --strategy-option ours --no-commit --allow-unrelated-histories
lake update batteries
git add lake-manifest.json
git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}" git commit --allow-empty -m "Trigger CI for https://github.com/leanprover/lean4/pull/${{ steps.workflow-info.outputs.pullRequestNumber }}"
fi fi

View File

@@ -7,29 +7,25 @@ on:
jobs: jobs:
restart-on-label: restart-on-label:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: contains(github.event.label.name, 'merge-ci') || contains(github.event.label.name, 'release-ci') if: contains(github.event.label.name, 'full-ci')
steps: steps:
- run: | - run: |
# Finding latest CI workflow run on current pull request # Finding latest CI workflow run on current pull request
# (unfortunately cannot search by PR number, only base branch, # (unfortunately cannot search by PR number, only base branch,
# and that is't even unique given PRs from forks, but the risk # and that is't even unique given PRs from forks, but the risk
# of confusion is low and the danger is mild) # of confusion is low and the danger is mild)
echo "Trying to find a run with branch $head_ref and commit $head_sha" run_id=$(gh run list -e pull_request -b "$head_ref" --workflow 'CI' --limit 1 \
run_id="$(gh run list -e pull_request -b "$head_ref" -c "$head_sha" \ --limit 1 --json databaseId --jq '.[0].databaseId')
--workflow 'CI' --limit 1 --json databaseId --jq '.[0].databaseId')"
echo "Run id: ${run_id}" echo "Run id: ${run_id}"
gh run view "$run_id" gh run view "$run_id"
echo "Cancelling (just in case)" echo "Cancelling (just in case)"
gh run cancel "$run_id" || echo "(failed)" gh run cancel "$run_id" || echo "(failed)"
echo "Waiting for 30s" echo "Waiting for 10s"
sleep 30 sleep 10
gh run view "$run_id"
echo "Rerunning" echo "Rerunning"
gh run rerun "$run_id" gh run rerun "$run_id"
gh run view "$run_id"
shell: bash shell: bash
env: env:
head_ref: ${{ github.head_ref }} head_ref: ${{ github.head_ref }}
head_sha: ${{ github.event.pull_request.head.sha }}
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }} GH_REPO: ${{ github.repository }}

View File

@@ -11,7 +11,7 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v9 - uses: actions/stale@v8
with: with:
days-before-stale: -1 days-before-stale: -1
days-before-pr-stale: 30 days-before-pr-stale: 30

View File

@@ -23,7 +23,7 @@ jobs:
# This action should push to an otherwise protected branch, so it # This action should push to an otherwise protected branch, so it
# uses a deploy key with write permissions, as suggested at # uses a deploy key with write permissions, as suggested at
# https://stackoverflow.com/a/76135647/946226 # https://stackoverflow.com/a/76135647/946226
- uses: actions/checkout@v4 - uses: actions/checkout@v3
with: with:
ssh-key: ${{secrets.STAGE0_SSH_KEY}} ssh-key: ${{secrets.STAGE0_SSH_KEY}}
- run: echo "should_update_stage0=yes" >> "$GITHUB_ENV" - run: echo "should_update_stage0=yes" >> "$GITHUB_ENV"
@@ -47,7 +47,7 @@ jobs:
# uses: DeterminateSystems/magic-nix-cache-action@v2 # uses: DeterminateSystems/magic-nix-cache-action@v2
- if: env.should_update_stage0 == 'yes' - if: env.should_update_stage0 == 'yes'
name: Restore Build Cache name: Restore Build Cache
uses: actions/cache/restore@v4 uses: actions/cache/restore@v3
with: with:
path: nix-store-cache path: nix-store-cache
key: Nix Linux-nix-store-cache-${{ github.sha }} key: Nix Linux-nix-store-cache-${{ github.sha }}

6
.gitignore vendored
View File

@@ -4,10 +4,8 @@
*.lock *.lock
.lake .lake
lake-manifest.json lake-manifest.json
/build build
/src/lakefile.toml !/src/lake/Lake/Build
/tests/lakefile.toml
/lakefile.toml
GPATH GPATH
GRTAGS GRTAGS
GSYMS GSYMS

View File

@@ -30,35 +30,6 @@ if(NOT (DEFINED STAGE0_CMAKE_EXECUTABLE_SUFFIX))
set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}") set(STAGE0_CMAKE_EXECUTABLE_SUFFIX "${CMAKE_EXECUTABLE_SUFFIX}")
endif() endif()
# Don't do anything with cadical on wasm
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# On CI Linux, we source cadical from Nix instead; see flake.nix
find_program(CADICAL cadical)
if(NOT CADICAL)
set(CADICAL_CXX c++)
find_program(CCACHE ccache)
if(CCACHE)
set(CADICAL_CXX "${CCACHE} ${CADICAL_CXX}")
endif()
# missing stdio locking API on Windows
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CADICAL_CXXFLAGS " -DNUNLOCKED")
endif()
ExternalProject_add(cadical
PREFIX cadical
GIT_REPOSITORY https://github.com/arminbiere/cadical
GIT_TAG rel-1.9.5
CONFIGURE_COMMAND ""
# https://github.com/arminbiere/cadical/blob/master/BUILD.md#manual-build
BUILD_COMMAND $(MAKE) -f ${CMAKE_SOURCE_DIR}/src/cadical.mk CMAKE_EXECUTABLE_SUFFIX=${CMAKE_EXECUTABLE_SUFFIX} CXX=${CADICAL_CXX} CXXFLAGS=${CADICAL_CXXFLAGS}
BUILD_IN_SOURCE ON
INSTALL_COMMAND "")
set(CADICAL ${CMAKE_BINARY_DIR}/cadical/cadical${CMAKE_EXECUTABLE_SUFFIX} CACHE FILEPATH "path to cadical binary" FORCE)
set(EXTRA_DEPENDS "cadical")
endif()
list(APPEND CL_ARGS -DCADICAL=${CADICAL})
endif()
ExternalProject_add(stage0 ExternalProject_add(stage0
SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0" SOURCE_DIR "${LEAN_SOURCE_DIR}/stage0"
SOURCE_SUBDIR src SOURCE_SUBDIR src

View File

@@ -1,83 +0,0 @@
{
"version": 2,
"cmakeMinimumRequired": {
"major": 3,
"minor": 10,
"patch": 0
},
"configurePresets": [
{
"name": "release",
"displayName": "Default development optimized build config",
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/release"
},
{
"name": "debug",
"displayName": "Debug build config",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/debug"
},
{
"name": "sanitize",
"displayName": "Sanitize build config",
"cacheVariables": {
"LEAN_EXTRA_CXX_FLAGS": "-fsanitize=address,undefined",
"LEANC_EXTRA_FLAGS": "-fsanitize=address,undefined -fsanitize-link-c++-runtime",
"SMALL_ALLOCATOR": "OFF",
"BSYMBOLIC": "OFF"
},
"generator": "Unix Makefiles",
"binaryDir": "${sourceDir}/build/sanitize"
},
{
"name": "sandebug",
"inherits": ["debug", "sanitize"],
"displayName": "Sanitize+debug build config",
"binaryDir": "${sourceDir}/build/sandebug"
}
],
"buildPresets": [
{
"name": "release",
"configurePreset": "release"
},
{
"name": "debug",
"configurePreset": "debug"
},
{
"name": "sanitize",
"configurePreset": "sanitize"
},
{
"name": "sandebug",
"configurePreset": "sandebug"
}
],
"testPresets": [
{
"name": "release",
"configurePreset": "release",
"output": {"outputOnFailure": true, "shortProgress": true}
},
{
"name": "debug",
"configurePreset": "debug",
"inherits": "release"
},
{
"name": "sanitize",
"configurePreset": "sanitize",
"inherits": "release"
},
{
"name": "sandebug",
"configurePreset": "sandebug",
"inherits": "release"
}
]
}

View File

@@ -4,20 +4,22 @@
# Listed persons will automatically be asked by GitHub to review a PR touching these paths. # Listed persons will automatically be asked by GitHub to review a PR touching these paths.
# If multiple names are listed, a review by any of them is considered sufficient by default. # If multiple names are listed, a review by any of them is considered sufficient by default.
/.github/ @kim-em /.github/ @Kha @semorrison
/RELEASES.md @kim-em /RELEASES.md @semorrison
/src/kernel/ @leodemoura /src/kernel/ @leodemoura
/src/lake/ @tydeu /src/lake/ @tydeu
/src/Lean/Compiler/ @leodemoura /src/Lean/Compiler/ @leodemoura
/src/Lean/Data/Lsp/ @mhuisi /src/Lean/Data/Lsp/ @mhuisi
/src/Lean/Elab/Deriving/ @kim-em /src/Lean/Elab/Deriving/ @semorrison
/src/Lean/Elab/Tactic/ @kim-em /src/Lean/Elab/Tactic/ @semorrison
/src/Lean/Language/ @Kha /src/Lean/Language/ @Kha
/src/Lean/Meta/Tactic/ @leodemoura /src/Lean/Meta/Tactic/ @leodemoura
/src/Lean/PrettyPrinter/ @kmill /src/Lean/Parser/ @Kha
/src/Lean/PrettyPrinter/ @Kha
/src/Lean/PrettyPrinter/Delaborator/ @kmill
/src/Lean/Server/ @mhuisi /src/Lean/Server/ @mhuisi
/src/Lean/Widget/ @Vtec234 /src/Lean/Widget/ @Vtec234
/src/Init/Data/ @kim-em /src/Init/Data/ @semorrison
/src/Init/Data/Array/Lemmas.lean @digama0 /src/Init/Data/Array/Lemmas.lean @digama0
/src/Init/Data/List/Lemmas.lean @digama0 /src/Init/Data/List/Lemmas.lean @digama0
/src/Init/Data/List/BasicAux.lean @digama0 /src/Init/Data/List/BasicAux.lean @digama0
@@ -40,7 +42,4 @@
/src/Lean/Elab/Tactic/Guard.lean @digama0 /src/Lean/Elab/Tactic/Guard.lean @digama0
/src/Init/Guard.lean @digama0 /src/Init/Guard.lean @digama0
/src/Lean/Server/CodeActions/ @digama0 /src/Lean/Server/CodeActions/ @digama0
/src/Std/ @TwoFX
/src/Std/Tactic/BVDecide/ @hargoniX
/src/Lean/Elab/Tactic/BVDecide/ @hargoniX
/src/Std/Sat/ @hargoniX

View File

@@ -63,20 +63,6 @@ Because the change will be squashed, there is no need to polish the commit messa
Reviews and Feedback: Reviews and Feedback:
---- ----
The lean4 repo is managed by the Lean FRO's *triage team* that aims to provide initial feedback on new bug reports, PRs, and RFCs weekly.
This feedback generally consists of prioritizing the ticket using one of the following categories:
* label `P-high`: We will work on this issue
* label `P-medium`: We may work on this issue if we find the time
* label `P-low`: We are not planning to work on this issue
* *closed*: This issue is already fixed, it is not an issue, or is not sufficiently compatible with our roadmap for the project and we will not work on it nor accept external contributions on it
For *bug reports*, the listed priority reflects our commitment to fixing the issue.
It is generally indicative but not necessarily identical to the priority an external contribution addressing this bug would receive.
For *PRs* and *RFCs*, the priority reflects our commitment to reviewing them and getting them to an acceptable state.
Accepted RFCs are marked with the label `RFC accepted` and afterwards assigned a new "implementation" priority as with bug reports.
General guidelines for interacting with reviews and feedback:
**Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time. **Be Patient**: Given the limited number of full-time maintainers and the volume of PRs, reviews may take some time.
**Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism. **Engage Constructively**: Always approach feedback positively and constructively. Remember, reviews are about ensuring the best quality for the project, not personal criticism.

View File

@@ -1341,33 +1341,3 @@ whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the permanent authorization for you to choose that version for the
Library. Library.
==============================================================================
CaDiCaL is under the MIT License:
==============================================================================
MIT License
Copyright (c) 2016-2021 Armin Biere, Johannes Kepler University Linz, Austria
Copyright (c) 2020-2021 Mathias Fleury, Johannes Kepler University Linz, Austria
Copyright (c) 2020-2021 Nils Froleyks, Johannes Kepler University Linz, Austria
Copyright (c) 2022-2024 Katalin Fazekas, Vienna University of Technology, Austria
Copyright (c) 2021-2024 Armin Biere, University of Freiburg, Germany
Copyright (c) 2021-2024 Mathias Fleury, University of Freiburg, Germany
Copyright (c) 2023-2024 Florian Pollitt, University of Freiburg, Germany
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1 @@
# Characters # Characters
A value of type `Char`, also known as a character, is a [Unicode scalar value](https://www.unicode.org/glossary/#unicode_scalar_value). It is represented using an unsigned 32-bit integer and is statically guaranteed to be a valid Unicode scalar value.
Syntactically, character literals are enclosed in single quotes.
```lean
#eval 'a' -- 'a'
#eval '' -- '∀'
```
Characters are ordered and can be decidably compared using the relational operators `=`, `<`, `≤`, `>`, `≥`.

View File

@@ -73,7 +73,7 @@ update the archived C source code of the stage 0 compiler in `stage0/src`.
The github repository will automatically update stage0 on `master` once The github repository will automatically update stage0 on `master` once
`src/stdlib_flags.h` and `stage0/src/stdlib_flags.h` are out of sync. `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 manually 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. trigger that process, for example to be able to use new features in the compiler itself.
You can do that on <https://github.com/leanprover/lean4/actions/workflows/update-stage0.yml> You can do that on <https://github.com/leanprover/lean4/actions/workflows/update-stage0.yml>
or using Github CLI with or using Github CLI with
@@ -103,21 +103,10 @@ your PR using rebase merge, bypassing the merge queue.
As written above, changes in meta code in the current stage usually will only As written above, changes in meta code in the current stage usually will only
affect later stages. This is an issue in two specific cases. affect later stages. This is an issue in two specific cases.
* For the special case of *quotations*, it is desirable to have changes in builtin parsers affect them immediately: when the changes in the parser become active in the next stage, builtin macros implemented via quotations should generate syntax trees compatible with the new parser, and quotation patterns in builtin macros and elaborators should be able to match syntax created by the new parser and macros.
Since quotations capture the syntax tree structure during execution of the current stage and turn it into code for the next stage, we need to run the current stage's builtin parsers in quotations via the interpreter for this to work.
Caveats:
* We activate this behavior by default when building stage 1 by setting `-Dinternal.parseQuotWithCurrentStage=true`.
We force-disable it inside `macro/macro_rules/elab/elab_rules` via `suppressInsideQuot` as they are guaranteed not to run in the next stage and may need to be run in the current one, so the stage 0 parser is the correct one to use for them.
It may be necessary to extend this disabling to functions that contain quotations and are (exclusively) used by one of the mentioned commands. A function using quotations should never be used by both builtin and non-builtin macros/elaborators. Example: https://github.com/leanprover/lean4/blob/f70b7e5722da6101572869d87832494e2f8534b7/src/Lean/Elab/Tactic/Config.lean#L118-L122
* The parser needs to be reachable via an `import` statement, otherwise the version of the previous stage will silently be used.
* Only the parser code (`Parser.fn`) is affected; all metadata such as leading tokens is taken from the previous stage.
For an example, see https://github.com/leanprover/lean4/commit/f9dcbbddc48ccab22c7674ba20c5f409823b4cc1#diff-371387aed38bb02bf7761084fd9460e4168ae16d1ffe5de041b47d3ad2d22422R13
* For *non-builtin* meta code such as `notation`s or `macro`s in * For *non-builtin* meta code such as `notation`s or `macro`s in
`Notation.lean`, we expect changes to affect the current file and all later `Notation.lean`, we expect changes to affect the current file and all later
files of the same stage immediately, just like outside the stdlib. To ensure files of the same stage immediately, just like outside the stdlib. To ensure
this, we build stage 1 using `-Dinterpreter.prefer_native=false` - this, we need to build the stage using `-Dinterpreter.prefer_native=false` -
otherwise, when executing a macro, the interpreter would notice that there is otherwise, when executing a macro, the interpreter would notice that there is
already a native symbol available for this function and run it instead of the already a native symbol available for this function and run it instead of the
new IR, but the symbol is from the previous stage! new IR, but the symbol is from the previous stage!
@@ -135,11 +124,26 @@ affect later stages. This is an issue in two specific cases.
further stages (e.g. after an `update-stage0`) will then need to be compiled further stages (e.g. after an `update-stage0`) will then need to be compiled
with the flag set to `false` again since they will expect the new signature. with the flag set to `false` again since they will expect the new signature.
When enabling `prefer_native`, we usually want to *disable* `parseQuotWithCurrentStage` as it would otherwise make quotations use the interpreter after all. For an example, see https://github.com/leanprover/lean4/commit/da4c46370d85add64ef7ca5e7cc4638b62823fbb.
However, there is a specific case where we want to set both options to `true`: when we make changes to a non-builtin parser like `simp` that has a builtin elaborator, we cannot have the new parser be active outside of quotations in stage 1 as the builtin elaborator from stage 0 would not understand them; on the other hand, we need quotations in e.g. the builtin `simp` elaborator to produce the new syntax in the next stage.
As this issue usually affects only tactics, enabling `debug.byAsSorry` instead of `prefer_native` can be a simpler solution.
For a `prefer_native` example, see https://github.com/leanprover/lean4/commit/da4c46370d85add64ef7ca5e7cc4638b62823fbb. * For the special case of *quotations*, it is desirable to have changes in
built-in parsers affect them immediately: when the changes in the parser
become active in the next stage, macros implemented via quotations should
generate syntax trees compatible with the new parser, and quotation patterns
in macro and elaborators should be able to match syntax created by the new
parser and macros. Since quotations capture the syntax tree structure during
execution of the current stage and turn it into code for the next stage, we
need to run the current stage's built-in parsers in quotation via the
interpreter for this to work. Caveats:
* Since interpreting full parsers is not nearly as cheap and we rarely change
built-in syntax, this needs to be opted in using `-Dinternal.parseQuotWithCurrentStage=true`.
* The parser needs to be reachable via an `import` statement, otherwise the
version of the previous stage will silently be used.
* Only the parser code (`Parser.fn`) is affected; all metadata such as leading
tokens is taken from the previous stage.
For an example, see https://github.com/leanprover/lean4/commit/f9dcbbddc48ccab22c7674ba20c5f409823b4cc1#diff-371387aed38bb02bf7761084fd9460e4168ae16d1ffe5de041b47d3ad2d22422
(from before the flag defaulted to `false`).
To modify either of these flags both for building and editing the stdlib, adjust To modify either of these flags both for building and editing the stdlib, adjust
the code in `stage0/src/stdlib_flags.h`. The flags will automatically be reset the code in `stage0/src/stdlib_flags.h`. The flags will automatically be reset

View File

@@ -5,7 +5,7 @@ Some notes on how to debug Lean, which may also be applicable to debugging Lean
## Tracing ## Tracing
In `CoreM` and derived monads, we use `trace[traceCls] "msg with {interpolations}"` to fill the structured trace viewable with `set_option trace.traceCls true`. In `CoreM` and derived monads, we use `trace![traceCls] "msg with {interpolations}"` to fill the structured trace viewable with `set_option trace.traceCls true`.
New trace classes have to be registered using `registerTraceClass` first. New trace classes have to be registered using `registerTraceClass` first.
Notable trace classes: Notable trace classes:
@@ -22,9 +22,7 @@ Notable trace classes:
In pure contexts or when execution is aborted before the messages are finally printed, one can instead use the term `dbg_trace "msg with {interpolations}"; val` (`;` can also be replaced by a newline), which will print the message to stderr before evaluating `val`. `dbgTraceVal val` can be used as a shorthand for `dbg_trace "{val}"; val`. In pure contexts or when execution is aborted before the messages are finally printed, one can instead use the term `dbg_trace "msg with {interpolations}"; val` (`;` can also be replaced by a newline), which will print the message to stderr before evaluating `val`. `dbgTraceVal val` can be used as a shorthand for `dbg_trace "{val}"; val`.
Note that if the return value is not actually used, the trace code is silently dropped as well. Note that if the return value is not actually used, the trace code is silently dropped as well.
In the language server, stderr output is buffered and shown as messages after a command has been elaborated, unless the option `server.stderrAsMessages` is deactivated.
By default, such stderr output is buffered and shown as messages after a command has been elaborated, which is necessary to ensure deterministic ordering of messages under parallelism.
If Lean aborts the process before it can finish the command or takes too long to do that, using `-DstderrAsMessages=false` avoids this buffering and shows `dbg_trace` output (but not `trace`s or other diagnostics) immediately.
## Debuggers ## Debuggers

View File

@@ -5,11 +5,7 @@ See below for the checklist for release candidates.
We'll use `v4.6.0` as the intended release version as a running example. We'll use `v4.6.0` as the intended release version as a running example.
- One week before the planned release, ensure that - One week before the planned release, ensure that someone has written the first draft of the release blog post
(1) someone has written the release notes and
(2) someone has written the first draft of the release blog post.
If there is any material in `./releases_drafts/` on the `releases/v4.6.0` branch, then the release notes are not done.
(See the section "Writing the release notes".)
- `git checkout releases/v4.6.0` - `git checkout releases/v4.6.0`
(This branch should already exist, from the release candidates.) (This branch should already exist, from the release candidates.)
- `git pull` - `git pull`
@@ -17,6 +13,13 @@ We'll use `v4.6.0` as the intended release version as a running example.
- `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate) - `set(LEAN_VERSION_MINOR 6)` (for whichever `6` is appropriate)
- `set(LEAN_VERSION_IS_RELEASE 1)` - `set(LEAN_VERSION_IS_RELEASE 1)`
- (both of these should already be in place from the release candidates) - (both of these should already be in place from the release candidates)
- It is possible that the `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Run `git diff master RELEASES.md`.
- You should expect to see additons on `master` in the `v4.7.0-rc1` section; ignore these.
(i.e. the new release notes for the upcoming release candidate).
- Reconcile discrepancies in the `v4.6.0` section,
usually via copy and paste and a commit to `releases/v4.6.0`.
- `git tag v4.6.0` - `git tag v4.6.0`
- `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`) - `git push $REMOTE v4.6.0`, where `$REMOTE` is the upstream Lean repository (e.g., `origin`, `upstream`)
- Now wait, while CI runs. - Now wait, while CI runs.
@@ -27,9 +30,8 @@ We'll use `v4.6.0` as the intended release version as a running example.
you may want to start on the release candidate checklist now. you may want to start on the release candidate checklist now.
- Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears. - Go to https://github.com/leanprover/lean4/releases and verify that the `v4.6.0` release appears.
- Edit the release notes on Github to select the "Set as the latest release". - Edit the release notes on Github to select the "Set as the latest release".
- Follow the instructions in creating a release candidate for the "GitHub release notes" step, - Copy and paste the Github release notes from the previous releases candidate for this version
now that we have a written `RELEASES.md` section. (e.g. `v4.6.0-rc1`), and quickly sanity check.
Do a quick sanity check.
- Next, we will move a curated list of downstream repos to the latest stable release. - Next, we will move a curated list of downstream repos to the latest stable release.
- For each of the repositories listed below: - For each of the repositories listed below:
- Make a PR to `master`/`main` changing the toolchain to `v4.6.0` - Make a PR to `master`/`main` changing the toolchain to `v4.6.0`
@@ -44,6 +46,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
- We do this for the repositories: - We do this for the repositories:
- [lean4checker](https://github.com/leanprover/lean4checker) - [lean4checker](https://github.com/leanprover/lean4checker)
- No dependencies - No dependencies
- Note: `lean4checker` uses a different version tagging scheme: use `toolchain/v4.6.0` rather than `v4.6.0`.
- Toolchain bump PR - Toolchain bump PR
- Create and push the tag - Create and push the tag
- Merge the tag into `stable` - Merge the tag into `stable`
@@ -71,12 +74,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Toolchain bump PR including updated Lake manifest - Toolchain bump PR including updated Lake manifest
- Create and push the tag - Create and push the tag
- There is no `stable` branch; skip this step - There is no `stable` branch; skip this step
- [Verso](https://github.com/leanprover/verso)
- Dependencies: exist, but they're not part of the release workflow
- The `SubVerso` dependency should be compatible with _every_ Lean release simultaneously, rather than following this workflow
- Toolchain bump PR including updated Lake manifest
- Create and push the tag
- There is no `stable` branch; skip this step
- [import-graph](https://github.com/leanprover-community/import-graph) - [import-graph](https://github.com/leanprover-community/import-graph)
- Toolchain bump PR including updated Lake manifest - Toolchain bump PR including updated Lake manifest
- Create and push the tag - Create and push the tag
@@ -85,8 +82,10 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Dependencies: `Aesop`, `ProofWidgets4`, `lean4checker`, `Batteries`, `doc-gen4`, `import-graph` - Dependencies: `Aesop`, `ProofWidgets4`, `lean4checker`, `Batteries`, `doc-gen4`, `import-graph`
- Toolchain bump PR notes: - Toolchain bump PR notes:
- In addition to updating the `lean-toolchain` and `lakefile.lean`, - In addition to updating the `lean-toolchain` and `lakefile.lean`,
in `.github/workflows/lean4checker.yml` update the line in `.github/workflows/build.yml.in` in the `lean4checker` section update the line
`git checkout v4.6.0` to the appropriate tag. `git checkout toolchain/v4.6.0` to the appropriate tag,
and then run `.github/workflows/mk_build_yml.sh`. Coordinate with
a Mathlib maintainer to get this merged.
- Push the PR branch to the main Mathlib repository rather than a fork, or CI may not work reliably - Push the PR branch to the main Mathlib repository rather than a fork, or CI may not work reliably
- Create and push the tag - Create and push the tag
- Create a new branch from the tag, push it, and open a pull request against `stable`. - Create a new branch from the tag, push it, and open a pull request against `stable`.
@@ -98,10 +97,6 @@ We'll use `v4.6.0` as the intended release version as a running example.
- Toolchain bump PR including updated Lake manifest - Toolchain bump PR including updated Lake manifest
- Create and push the tag - Create and push the tag
- Merge the tag into `stable` - Merge the tag into `stable`
- The `v4.6.0` section of `RELEASES.md` is out of sync between
`releases/v4.6.0` and `master`. This should be reconciled:
- Replace the `v4.6.0` section on `master` with the `v4.6.0` section on `releases/v4.6.0`
and commit this to `master`.
- Merge the release announcement PR for the Lean website - it will be deployed automatically - Merge the release announcement PR for the Lean website - it will be deployed automatically
- Finally, make an announcement! - Finally, make an announcement!
This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`. This should go in https://leanprover.zulipchat.com/#narrow/stream/113486-announce, with topic `v4.6.0`.
@@ -112,6 +107,7 @@ We'll use `v4.6.0` as the intended release version as a running example.
## Optimistic(?) time estimates: ## Optimistic(?) time estimates:
- Initial checks and push the tag: 30 minutes. - Initial checks and push the tag: 30 minutes.
- Note that if `RELEASES.md` has discrepancies this could take longer!
- Waiting for the release: 60 minutes. - Waiting for the release: 60 minutes.
- Fixing release notes: 10 minutes. - Fixing release notes: 10 minutes.
- Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes. - Bumping toolchains in downstream repositories, up to creating the Mathlib PR: 30 minutes.
@@ -138,52 +134,54 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
git checkout nightly-2024-02-29 git checkout nightly-2024-02-29
git checkout -b releases/v4.7.0 git checkout -b releases/v4.7.0
``` ```
- In `RELEASES.md` replace `Development in progress` in the `v4.7.0` section with `Release notes to be written.` - In `RELEASES.md` remove `(development in progress)` from the `v4.7.0` section header.
- We will rely on automatically generated release notes for release candidates, - Our current goal is to have written release notes only about major language features or breaking changes,
and the written release notes will be used for stable versions only. and to rely on automatically generated release notes for bugfixes and minor changes.
It is essential to choose the nightly that will become the release candidate as early as possible, to avoid confusion. - Do not wait on `RELEASES.md` being perfect before creating the `release/v4.7.0` branch. It is essential to choose the nightly which will become the release candidate as early as possible, to avoid confusion.
- If there are major changes not reflected in `RELEASES.md` already, you may need to solicit help from the authors.
- Minor changes and bug fixes do not need to be documented in `RELEASES.md`: they will be added automatically on the Github release page.
- Commit your changes to `RELEASES.md`, and push.
- Remember that changes to `RELEASES.md` after you have branched `releases/v4.7.0` should also be cherry-picked back to `master`.
- In `src/CMakeLists.txt`, - In `src/CMakeLists.txt`,
- verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began. - verify that you see `set(LEAN_VERSION_MINOR 7)` (for whichever `7` is appropriate); this should already have been updated when the development cycle began.
- `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`). - `set(LEAN_VERSION_IS_RELEASE 1)` (this should be a change; on `master` and nightly releases it is always `0`).
- Commit your changes to `src/CMakeLists.txt`, and push. - Commit your changes to `src/CMakeLists.txt`, and push.
- `git tag v4.7.0-rc1` - `git tag v4.7.0-rc1`
- `git push origin v4.7.0-rc1` - `git push origin v4.7.0-rc1`
- Ping the FRO Zulip that release notes need to be written. The release notes do not block completing the rest of this checklist.
- Now wait, while CI runs. - Now wait, while CI runs.
- You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag. - You can monitor this at `https://github.com/leanprover/lean4/actions/workflows/ci.yml`, looking for the `v4.7.0-rc1` tag.
- This step can take up to an hour. - This step can take up to an hour.
- (GitHub release notes) Once the release appears at https://github.com/leanprover/lean4/releases/ - Once the release appears at https://github.com/leanprover/lean4/releases/
- Verify that the release is marked as a prerelease (this should have been done automatically by the CI release job). - Edit the release notes on Github to select the "Set as a pre-release box".
- In the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes". - Copy the section of `RELEASES.md` for this version into the Github release notes.
This will add a list of all the commits since the last stable version. - Use the title "Changes since v4.6.0 (from RELEASES.md)"
- Then in the "previous tag" dropdown, select `v4.6.0`, and click "Generate release notes".
- This will add a list of all the commits since the last stable version.
- Delete anything already mentioned in the hand-written release notes above.
- Delete "update stage0" commits, and anything with a completely inscrutable commit message. - Delete "update stage0" commits, and anything with a completely inscrutable commit message.
- Briefly rearrange the remaining items by category (e.g. `simp`, `lake`, `bug fixes`),
but for minor items don't put any work in expanding on commit messages.
- (How we want to release notes to look is evolving: please update this section if it looks wrong!)
- Next, we will move a curated list of downstream repos to the release candidate. - Next, we will move a curated list of downstream repos to the release candidate.
- This assumes that for each repository either: - This assumes that there is already a *reviewed* branch `bump/v4.7.0` on each repository
* There is already a *reviewed* branch `bump/v4.7.0` containing the required adaptations. containing the required adaptations (or no adaptations are required).
The preparation of this branch is beyond the scope of this document. The preparation of this branch is beyond the scope of this document.
* The repository does not need any changes to move to the new version.
- For each of the target repositories: - For each of the target repositories:
- If the repository does not need any changes (i.e. `bump/v4.7.0` does not exist) then create - Checkout the `bump/v4.7.0` branch.
a new PR updating `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1` and running `lake update`. - Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created.
- Otherwise: - `git merge origin/master`
- Checkout the `bump/v4.7.0` branch. - Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1`
- Verify that the `lean-toolchain` is set to the nightly from which the release candidate was created. - In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches
- `git merge origin/master` back to `master` or `main`, and run `lake update` for those dependencies.
- Change the `lean-toolchain` to `leanprover/lean4:v4.7.0-rc1` - Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment).
- In `lakefile.lean`, change any dependencies which were using `nightly-testing` or `bump/v4.7.0` branches - `git commit`
back to `master` or `main`, and run `lake update` for those dependencies. - `git push`
- Run `lake build` to ensure that dependencies are found (but it's okay to stop it after a moment). - Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
- `git commit` or notify the maintainers that it is ready to go.
- `git push` - Once this PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
- Open a PR from `bump/v4.7.0` to `master`, and either merge it yourself after CI, if appropriate,
or notify the maintainers that it is ready to go.
- Once the PR has been merged, tag `master` with `v4.7.0-rc1` and push this tag.
- We do this for the same list of repositories as for stable releases, see above. - We do this for the same list of repositories as for stable releases, see above.
As above, there are dependencies between these, and so the process above is iterative. As above, there are dependencies between these, and so the process above is iterative.
It greatly helps if you can merge the `bump/v4.7.0` PRs yourself! It greatly helps if you can merge the `bump/v4.7.0` PRs yourself!
It is essential for Mathlib CI that you then create the next `bump/v4.8.0` branch
for the next development cycle.
Set the `lean-toolchain` file on this branch to same `nightly` you used for this release.
- For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag - For Batteries/Aesop/Mathlib, which maintain a `nightly-testing` branch, make sure there is a tag
`nightly-testing-2024-02-29` with date corresponding to the nightly used for the release `nightly-testing-2024-02-29` with date corresponding to the nightly used for the release
(create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push. (create it if not), and then on the `nightly-testing` branch `git reset --hard master`, and force push.
@@ -194,21 +192,8 @@ We'll use `v4.7.0-rc1` as the intended release version in this example.
Please also make sure that whoever is handling social media knows the release is out. Please also make sure that whoever is handling social media knows the release is out.
- Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that: - Begin the next development cycle (i.e. for `v4.8.0`) on the Lean repository, by making a PR that:
- Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)` - Updates `src/CMakeLists.txt` to say `set(LEAN_VERSION_MINOR 8)`
- Replaces the "release notes will be copied" text in the `v4.6.0` section of `RELEASES.md` with the - Removes `(in development)` from the section heading in `RELEASES.md` for `v4.7.0`,
finalized release notes from the `releases/v4.6.0` branch. and creates a new `v4.8.0 (in development)` section heading.
- Replaces the "development in progress" in the `v4.7.0` section of `RELEASES.md` with
```
Release candidate, release notes will be copied from the branch `releases/v4.7.0` once completed.
```
and inserts the following section before that section:
```
v4.8.0
----------
Development in progress.
```
- Removes all the entries from the `./releases_drafts/` folder.
- Titled "chore: begin development cycle for v4.8.0"
## Time estimates: ## Time estimates:
Slightly longer than the corresponding steps for a stable release. Slightly longer than the corresponding steps for a stable release.
@@ -233,30 +218,12 @@ Please read https://leanprover-community.github.io/contribute/tags_and_branches.
* This can either be done by the person managing this process directly, * This can either be done by the person managing this process directly,
or by soliciting assistance from authors of files, or generally helpful people on Zulip! or by soliciting assistance from authors of files, or generally helpful people on Zulip!
* Each repo has a `bump/v4.7.0` which accumulates reviewed changes adapting to new versions. * Each repo has a `bump/v4.7.0` which accumulates reviewed changes adapting to new versions.
* Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we will create a PR to `bump/v4.7.0`. * Once `nightly-testing` is working on a given nightly, say `nightly-2024-02-15`, we:
* For Mathlib, there is a script in `scripts/create-adaptation-pr.sh` that automates this process.
* For Batteries and Aesop it is currently manual.
* For all of these repositories, the process is the same:
* Make sure `bump/v4.7.0` is up to date with `master` (by merging `master`, no PR necessary) * Make sure `bump/v4.7.0` is up to date with `master` (by merging `master`, no PR necessary)
* Create from `bump/v4.7.0` a `bump/nightly-2024-02-15` branch. * Create from `bump/v4.7.0` a `bump/nightly-2024-02-15` branch.
* In that branch, `git merge nightly-testing` to bring across changes from `nightly-testing`. * In that branch, `git merge --squash nightly-testing` to bring across changes from `nightly-testing`.
* Sanity check changes, commit, and make a PR to `bump/v4.7.0` from the `bump/nightly-2024-02-15` branch. * Sanity check changes, commit, and make a PR to `bump/v4.7.0` from the `bump/nightly-2024-02-15` branch.
* Solicit review, merge the PR into `bump/v4.7.0`. * Solicit review, merge the PR into `bump/v4,7,0`.
* It is always okay to merge in the following directions: * It is always okay to merge in the following directions:
`master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`. `master` -> `bump/v4.7.0` -> `bump/nightly-2024-02-15` -> `nightly-testing`.
Please remember to push any merges you make to intermediate steps! Please remember to push any merges you make to intermediate steps!
# Writing the release notes
We are currently trying a system where release notes are compiled all at once from someone looking through the commit history.
The exact steps are a work in progress.
Here is the general idea:
* The work is done right on the `releases/v4.6.0` branch sometime after it is created but before the stable release is made.
The release notes for `v4.6.0` will later be copied to `master` when we begin a new development cycle.
* There can be material for release notes entries in commit messages.
* There can also be pre-written entries in `./releases_drafts`, which should be all incorporated in the release notes and then deleted from the branch.
See `./releases_drafts/README.md` for more information.
* The release notes should be written from a downstream expert user's point of view.
This section will be updated when the next release notes are written (for `v4.10.0`).

View File

@@ -18,7 +18,7 @@ def ctor (mvarId : MVarId) (idx : Nat) : MetaM (List MVarId) := do
else if h : idx - 1 < ctors.length then else if h : idx - 1 < ctors.length then
mvarId.apply (.const ctors[idx - 1] us) mvarId.apply (.const ctors[idx - 1] us)
else else
throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} constructors" throwTacticEx `ctor mvarId "invalid index, inductive datatype has only {ctors.length} contructors"
open Elab Tactic open Elab Tactic

View File

@@ -1 +0,0 @@
build

View File

@@ -149,7 +149,7 @@ We now define the constant folding optimization that traverses a term if replace
/-! /-!
The correctness of the `Term.constFold` is proved using induction, case-analysis, and the term simplifier. The correctness of the `Term.constFold` is proved using induction, case-analysis, and the term simplifier.
We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to
use hypotheses such as `a = b` as rewriting/simplifications rules. use hypotheses such as `a = b` as rewriting/simplications rules.
We use the `split` to break the nested `match` expression in the `plus` case into two cases. We use the `split` to break the nested `match` expression in the `plus` case into two cases.
The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`. The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`.
The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in

View File

@@ -12,17 +12,17 @@ Remark: this example is based on an example found in the Idris manual.
Vectors Vectors
-------- --------
A `Vec` is a list of size `n` whose elements belong to a type `α`. A `Vector` is a list of size `n` whose elements belong to a type `α`.
-/ -/
inductive Vec (α : Type u) : Nat Type u inductive Vector (α : Type u) : Nat Type u
| nil : Vec α 0 | nil : Vector α 0
| cons : α Vec α n Vec α (n+1) | cons : α Vector α n Vector α (n+1)
/-! /-!
We can overload the `List.cons` notation `::` and use it to create `Vec`s. We can overload the `List.cons` notation `::` and use it to create `Vector`s.
-/ -/
infix:67 " :: " => Vec.cons infix:67 " :: " => Vector.cons
/-! /-!
Now, we define the types of our simple functional language. Now, we define the types of our simple functional language.
@@ -50,11 +50,11 @@ the builtin instance for `Add Int` as the solution.
/-! /-!
Expressions are indexed by the types of the local variables, and the type of the expression itself. Expressions are indexed by the types of the local variables, and the type of the expression itself.
-/ -/
inductive HasType : Fin n Vec Ty n Ty Type where inductive HasType : Fin n Vector Ty n Ty Type where
| stop : HasType 0 (ty :: ctx) ty | stop : HasType 0 (ty :: ctx) ty
| pop : HasType k ctx ty HasType k.succ (u :: ctx) ty | pop : HasType k ctx ty HasType k.succ (u :: ctx) ty
inductive Expr : Vec Ty n Ty Type where inductive Expr : Vector Ty n Ty Type where
| var : HasType i ctx ty Expr ctx ty | var : HasType i ctx ty Expr ctx ty
| val : Int Expr ctx Ty.int | val : Int Expr ctx Ty.int
| lam : Expr (a :: ctx) ty Expr ctx (Ty.fn a ty) | lam : Expr (a :: ctx) ty Expr ctx (Ty.fn a ty)
@@ -102,8 +102,8 @@ indexed over the types in scope. Since an environment is just another form of li
to the vector of local variable types, we overload again the notation `::` so that we can use the usual list syntax. to the vector of local variable types, we overload again the notation `::` so that we can use the usual list syntax.
Given a proof that a variable is defined in the context, we can then produce a value from the environment. Given a proof that a variable is defined in the context, we can then produce a value from the environment.
-/ -/
inductive Env : Vec Ty n Type where inductive Env : Vector Ty n Type where
| nil : Env Vec.nil | nil : Env Vector.nil
| cons : Ty.interp a Env ctx Env (a :: ctx) | cons : Ty.interp a Env ctx Env (a :: ctx)
infix:67 " :: " => Env.cons infix:67 " :: " => Env.cons
@@ -149,4 +149,4 @@ def fact : Expr ctx (Ty.fn Ty.int Ty.int) :=
(op (·*·) (delay fun _ => app fact (op (·-·) (var stop) (val 1))) (var stop))) (op (·*·) (delay fun _ => app fact (op (·-·) (var stop) (val 1))) (var stop)))
decreasing_by sorry decreasing_by sorry
#eval! fact.interp Env.nil 10 #eval fact.interp Env.nil 10

View File

@@ -225,7 +225,7 @@ We now define the constant folding optimization that traverses a term if replace
/-! /-!
The correctness of the `constFold` is proved using induction, case-analysis, and the term simplifier. The correctness of the `constFold` is proved using induction, case-analysis, and the term simplifier.
We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to We prove all cases but the one for `plus` using `simp [*]`. This tactic instructs the term simplifier to
use hypotheses such as `a = b` as rewriting/simplifications rules. use hypotheses such as `a = b` as rewriting/simplications rules.
We use the `split` to break the nested `match` expression in the `plus` case into two cases. We use the `split` to break the nested `match` expression in the `plus` case into two cases.
The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`. The local variables `iha` and `ihb` are the induction hypotheses for `a` and `b`.
The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in The modifier `←` in a term simplifier argument instructs the term simplifier to use the equation as a rewriting rule in

View File

@@ -29,7 +29,7 @@ inductive HasType : Expr → Ty → Prop
/-! /-!
We can easily show that if `e` has type `t₁` and type `t₂`, then `t₁` and `t₂` must be equal We can easily show that if `e` has type `t₁` and type `t₂`, then `t₁` and `t₂` must be equal
by using the `cases` tactic. This tactic creates a new subgoal for every constructor, by using the the `cases` tactic. This tactic creates a new subgoal for every constructor,
and automatically discharges unreachable cases. The tactic combinator `tac₁ <;> tac₂` applies and automatically discharges unreachable cases. The tactic combinator `tac₁ <;> tac₂` applies
`tac₂` to each subgoal produced by `tac₁`. Then, the tactic `rfl` is used to close all produced `tac₂` to each subgoal produced by `tac₁`. Then, the tactic `rfl` is used to close all produced
goals using reflexivity. goals using reflexivity.
@@ -82,7 +82,9 @@ theorem Expr.typeCheck_correct (h₁ : HasType e ty) (h₂ : e.typeCheck ≠ .un
/-! /-!
Now, we prove that if `Expr.typeCheck e` returns `Maybe.unknown`, then forall `ty`, `HasType e ty` does not hold. Now, we prove that if `Expr.typeCheck e` returns `Maybe.unknown`, then forall `ty`, `HasType e ty` does not hold.
The notation `e.typeCheck` is sugar for `Expr.typeCheck e`. Lean can infer this because we explicitly said that `e` has type `Expr`. The notation `e.typeCheck` is sugar for `Expr.typeCheck e`. Lean can infer this because we explicitly said that `e` has type `Expr`.
The proof is by induction on `e` and case analysis. Note that the tactic `simp [typeCheck]` is applied to all goal generated by the `induction` tactic, and closes The proof is by induction on `e` and case analysis. The tactic `rename_i` is used to to rename "inaccessible" variables.
We say a variable is inaccessible if it is introduced by a tactic (e.g., `cases`) or has been shadowed by another variable introduced
by the user. Note that the tactic `simp [typeCheck]` is applied to all goal generated by the `induction` tactic, and closes
the cases corresponding to the constructors `Expr.nat` and `Expr.bool`. the cases corresponding to the constructors `Expr.nat` and `Expr.bool`.
-/ -/
theorem Expr.typeCheck_complete {e : Expr} : e.typeCheck = .unknown ¬ HasType e ty := by theorem Expr.typeCheck_complete {e : Expr} : e.typeCheck = .unknown ¬ HasType e ty := by

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/usr/bin/env bash
source ../../tests/common.sh source ../../tests/common.sh
exec_check_raw lean -Dlinter.all=false "$f" exec_check lean -j 0 -Dlinter.all=false "$f"

View File

@@ -4,18 +4,15 @@ open Lean Widget
/-! /-!
# The user-widgets system # The user-widgets system
Proving and programming are inherently interactive tasks. Proving and programming are inherently interactive tasks. Lots of mathematical objects and data
Lots of mathematical objects and data structures are visual in nature. structures are visual in nature. *User widgets* let you associate custom interactive UIs with
*User widgets* let you associate custom interactive UIs sections of a Lean document. User widgets are rendered in the Lean infoview.
with sections of a Lean document.
User widgets are rendered in the Lean infoview.
![Rubik's cube](../images/widgets_rubiks.png) ![Rubik's cube](../images/widgets_rubiks.png)
## Trying it out ## Trying it out
To try it out, type in the following code and place your cursor over the `#widget` command. To try it out, simply type in the following code and place your cursor over the `#widget` command.
You can also [view this manual entry in the online editor](https://live.lean-lang.org/#url=https%3A%2F%2Fraw.githubusercontent.com%2Fleanprover%2Flean4%2Fmaster%2Fdoc%2Fexamples%2Fwidgets.lean).
-/ -/
@[widget_module] @[widget_module]
@@ -24,37 +21,38 @@ def helloWidget : Widget.Module where
import * as React from 'react'; import * as React from 'react';
export default function(props) { export default function(props) {
const name = props.name || 'world' const name = props.name || 'world'
return React.createElement('p', {}, 'Hello ' + name + '!') return React.createElement('p', {}, name + '!')
}" }"
#widget helloWidget #widget helloWidget
/-! /-!
If you want to dive into a full sample right away, check out If you want to dive into a full sample right away, check out
[`Rubiks`](https://github.com/leanprover-community/ProofWidgets4/blob/main/ProofWidgets/Demos/Rubiks.lean). [`RubiksCube`](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/).
This sample uses higher-level widget components from the ProofWidgets library.
Below, we'll explain the system piece by piece. Below, we'll explain the system piece by piece.
⚠️ WARNING: All of the user widget APIs are **unstable** and subject to breaking changes. ⚠️ WARNING: All of the user widget APIs are **unstable** and subject to breaking changes.
## Widget modules and instances ## Widget sources and instances
A [widget module](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/UserWidget.html#Lean.Widget.Module) A *widget source* is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules)
is a valid JavaScript [ESModule](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) which exports a [React component](https://reactjs.org/docs/components-and-props.html). To access
that can execute in the Lean infoview. React, the module must use `import * as React from 'react'`. Our first example of a widget source
Most widget modules export a [React component](https://reactjs.org/docs/components-and-props.html) is of course the value of `helloWidget.javascript`.
as the piece of user interface to be rendered.
To access React, the module can use `import * as React from 'react'`.
Our first example of a widget module is `helloWidget` above.
Widget modules must be registered with the `@[widget_module]` attribute.
A [widget instance](https://leanprover-community.github.io/mathlib4_docs/Lean/Widget/Types.html#Lean.Widget.WidgetInstance) We can register a widget source with the `@[widget]` attribute, giving it a friendlier name
is then the identifier of a widget module (e.g. `` `helloWidget ``) in the `name` field. This is bundled together in a `UserWidgetDefinition`.
bundled with a value for its props.
This value is passed as the argument to the React component. A *widget instance* is then the identifier of a `UserWidgetDefinition` (so `` `helloWidget ``,
In our first invocation of `#widget`, we set it to `.null`. not `"Hello"`) associated with a range of positions in the Lean source code. Widget instances
Try out what happens when you type in: are stored in the *infotree* in the same manner as other information about the source file
such as the type of every expression. In our example, the `#widget` command stores a widget instance
with the entire line as its range. We can think of a widget instance as an instruction for the
infoview: "when the user places their cursor here, please render the following widget".
Every widget instance also contains a `props : Json` value. This value is passed as an argument
to the React component. In our first invocation of `#widget`, we set it to `.null`. Try out what
happens when you type in:
-/ -/
structure HelloWidgetProps where structure HelloWidgetProps where
@@ -64,37 +62,21 @@ structure HelloWidgetProps where
#widget helloWidget with { name? := "<your name here>" : HelloWidgetProps } #widget helloWidget with { name? := "<your name here>" : HelloWidgetProps }
/-! /-!
Under the hood, widget instances are associated with a range of positions in the source file. 💡 NOTE: The RPC system presented below does not depend on JavaScript. However the primary use case
Widget instances are stored in the *infotree* is the web-based infoview in VSCode.
in the same manner as other information about the source file
such as the type of every expression.
In our example, the `#widget` command stores a widget instance
with the entire line as its range.
One can think of the infotree entry as an instruction for the infoview:
"when the user places their cursor here, please render the following widget".
-/
/-!
## Querying the Lean server ## Querying the Lean server
💡 NOTE: The RPC system presented below does not depend on JavaScript. Besides enabling us to create cool client-side visualizations, user widgets come with the ability
However, the primary use case is the web-based infoview in VSCode. to communicate with the Lean server. Thanks to this, they have the same metaprogramming capabilities
as custom elaborators or the tactic framework. To see this in action, let's implement a `#check`
command as a web input form. This example assumes some familiarity with React.
Besides enabling us to create cool client-side visualizations, The first thing we'll need is to create an *RPC method*. Meaning "Remote Procedure Call", this
user widgets have the ability to communicate with the Lean server. is basically a Lean function callable from widget code (possibly remotely over the internet).
Thanks to this, they have the same metaprogramming capabilities
as custom elaborators or the tactic framework.
To see this in action, let's implement a `#check` command as a web input form.
This example assumes some familiarity with React.
The first thing we'll need is to create an *RPC method*.
Meaning "Remote Procedure Call",this is a Lean function callable from widget code
(possibly remotely over the internet).
Our method will take in the `name : Name` of a constant in the environment and return its type. Our method will take in the `name : Name` of a constant in the environment and return its type.
By convention, we represent the input data as a `structure`. By convention, we represent the input data as a `structure`. Since it will be sent over from JavaScript,
Since it will be sent over from JavaScript, we need `FromJson` and `ToJson`. We'll see below why the position field is needed.
we need `FromJson` and `ToJson` instance.
We'll see why the position field is needed later.
-/ -/
structure GetTypeParams where structure GetTypeParams where
@@ -105,33 +87,25 @@ structure GetTypeParams where
deriving FromJson, ToJson deriving FromJson, ToJson
/-! /-!
After its argument structure, we define the `getType` method. After its arguments, we define the `getType` method. Every RPC method executes in the `RequestM`
RPCs method execute in the `RequestM` monad and must return a `RequestTask α` monad and must return a `RequestTask α` where `α` is its "actual" return type. The `Task` is so
where `α` is the "actual" return type. that requests can be handled concurrently. A first guess for `α` might be `Expr`. However,
The `Task` is so that requests can be handled concurrently. expressions in general can be large objects which depend on an `Environment` and `LocalContext`.
As a first guess, we'd use `Expr` as `α`. Thus we cannot directly serialize an `Expr` and send it to the widget. Instead, there are two
However, expressions in general can be large objects options:
which depend on an `Environment` and `LocalContext`. - One is to send a *reference* which points to an object residing on the server. From JavaScript's
Thus we cannot directly serialize an `Expr` and send it to JavaScript. point of view, references are entirely opaque, but they can be sent back to other RPC methods for
Instead, there are two options: further processing.
- Two is to pretty-print the expression and send its textual representation called `CodeWithInfos`.
This representation contains extra data which the infoview uses for interactivity. We take this
strategy here.
- One is to send a *reference* which points to an object residing on the server. RPC methods execute in the context of a file, but not any particular `Environment` so they don't
From JavaScript's point of view, references are entirely opaque, know about the available `def`initions and `theorem`s. Thus, we need to pass in a position at which
but they can be sent back to other RPC methods for further processing. we want to use the local `Environment`. This is why we store it in `GetTypeParams`. The `withWaitFindSnapAtPos`
- The other is to pretty-print the expression and send its textual representation called `CodeWithInfos`. method launches a concurrent computation whose job is to find such an `Environment` and a bit
This representation contains extra data which the infoview uses for interactivity. more information for us, in the form of a `snap : Snapshot`. With this in hand, we can call
We take this strategy here. `MetaM` procedures to find out the type of `name` and pretty-print it.
RPC methods execute in the context of a file,
but not of any particular `Environment`,
so they don't know about the available `def`initions and `theorem`s.
Thus, we need to pass in a position at which we want to use the local `Environment`.
This is why we store it in `GetTypeParams`.
The `withWaitFindSnapAtPos` method launches a concurrent computation
whose job is to find such an `Environment` for us,
in the form of a `snap : Snapshot`.
With this in hand, we can call `MetaM` procedures
to find out the type of `name` and pretty-print it.
-/ -/
open Server RequestM in open Server RequestM in
@@ -147,22 +121,18 @@ def getType (params : GetTypeParams) : RequestM (RequestTask CodeWithInfos) :=
/-! /-!
## Using infoview components ## Using infoview components
Now that we have all we need on the server side, let's write the widget module. Now that we have all we need on the server side, let's write the widget source. By importing
By importing `@leanprover/infoview`, widgets can render UI components used to implement the infoview itself. `@leanprover/infoview`, widgets can render UI components used to implement the infoview itself.
For example, the `<InteractiveCode>` component displays expressions For example, the `<InteractiveCode>` component displays expressions with `term : type` tooltips
with `term : type` tooltips as seen in the goal view. as seen in the goal view. We will use it to implement our custom `#check` display.
We will use it to implement our custom `#check` display.
⚠️ WARNING: Like the other widget APIs, the infoview JS API is **unstable** and subject to breaking changes. ⚠️ WARNING: Like the other widget APIs, the infoview JS API is **unstable** and subject to breaking changes.
The code below demonstrates useful parts of the API. The code below demonstrates useful parts of the API. To make RPC method calls, we use the `RpcContext`.
To make RPC method calls, we invoke the `useRpcSession` hook. The `useAsync` helper packs the results of a call into an `AsyncState` structure which indicates
The `useAsync` helper packs the results of an RPC call into an `AsyncState` structure whether the call has resolved successfully, has returned an error, or is still in-flight. Based
which indicates whether the call has resolved successfully, on this we either display an `InteractiveCode` with the type, `mapRpcError` the error in order
has returned an error, or is still in-flight. to turn it into a readable message, or show a `Loading..` message, respectively.
Based on this we either display an `InteractiveCode` component with the result,
`mapRpcError` the error in order to turn it into a readable message,
or show a `Loading..` message, respectively.
-/ -/
@[widget_module] @[widget_module]
@@ -170,10 +140,10 @@ def checkWidget : Widget.Module where
javascript := " javascript := "
import * as React from 'react'; import * as React from 'react';
const e = React.createElement; const e = React.createElement;
import { useRpcSession, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview'; import { RpcContext, InteractiveCode, useAsync, mapRpcError } from '@leanprover/infoview';
export default function(props) { export default function(props) {
const rs = useRpcSession() const rs = React.useContext(RpcContext)
const [name, setName] = React.useState('getType') const [name, setName] = React.useState('getType')
const st = useAsync(() => const st = useAsync(() =>
@@ -189,7 +159,7 @@ export default function(props) {
" "
/-! /-!
We can now try out the widget. Finally we can try out the widget.
-/ -/
#widget checkWidget #widget checkWidget
@@ -199,31 +169,30 @@ We can now try out the widget.
## Building widget sources ## Building widget sources
While typing JavaScript inline is fine for a simple example, While typing JavaScript inline is fine for a simple example, for real developments we want to use
for real developments we want to use packages from NPM, a proper build system, and JSX. packages from NPM, a proper build system, and JSX. Thus, most actual widget sources are built with
Thus, most actual widget sources are built with Lake and NPM. Lake and NPM. They consist of multiple files and may import libraries which don't work as ESModules
They consist of multiple files and may import libraries which don't work as ESModules by default. by default. On the other hand a widget source must be a single, self-contained ESModule in the form
On the other hand a widget module must be a single, self-contained ESModule in the form of a string. of a string. Readers familiar with web development may already have guessed that to obtain such a
Readers familiar with web development may already have guessed that to obtain such a string, we need a *bundler*. string, we need a *bundler*. Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/)
Two popular choices are [`rollup.js`](https://rollupjs.org/guide/en/) and [`esbuild`](https://esbuild.github.io/). If we go with `rollup.js`, to make a widget work with
and [`esbuild`](https://esbuild.github.io/). the infoview we need to:
If we go with `rollup.js`, to make a widget work with the infoview we need to:
- Set [`output.format`](https://rollupjs.org/guide/en/#outputformat) to `'es'`. - Set [`output.format`](https://rollupjs.org/guide/en/#outputformat) to `'es'`.
- [Externalize](https://rollupjs.org/guide/en/#external) `react`, `react-dom`, `@leanprover/infoview`. - [Externalize](https://rollupjs.org/guide/en/#external) `react`, `react-dom`, `@leanprover/infoview`.
These libraries are already loaded by the infoview so they should not be bundled. These libraries are already loaded by the infoview so they should not be bundled.
ProofWidgets provides a working `rollup.js` build configuration in In the RubiksCube sample, we provide a working `rollup.js` build configuration in
[rollup.config.js](https://github.com/leanprover-community/ProofWidgets4/blob/main/widget/rollup.config.js). [rollup.config.js](https://github.com/leanprover/lean4-samples/blob/main/RubiksCube/widget/rollup.config.js).
## Inserting text ## Inserting text
Besides making RPC calls, widgets can instruct the editor to carry out certain actions. We can also instruct the editor to insert text, copy text to the clipboard, or
We can insert text, copy text to the clipboard, or highlight a certain location in the document. reveal a certain location in the document.
To do this, use the `EditorContext` React context. To do this, use the `React.useContext(EditorContext)` React context.
This will return an `EditorConnection` This will return an `EditorConnection` whose `api` field contains a number of methods to
whose `api` field contains a number of methods that interact with the editor. interact with the text editor.
The full API can be viewed [here](https://github.com/leanprover/vscode-lean4/blob/master/lean4-infoview-api/src/infoviewApi.ts#L52). 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_module] @[widget_module]
@@ -243,4 +212,6 @@ export default function(props) {
} }
" "
/-! Finally, we can try this out: -/
#widget insertTextWidget #widget insertTextWidget

View File

@@ -396,7 +396,7 @@ Every expression in Lean has a natural computational interpretation, unless it i
* *β-reduction* : An expression ``(λ x, t) s`` β-reduces to ``t[s/x]``, that is, the result of replacing ``x`` by ``s`` in ``t``. * *β-reduction* : An expression ``(λ x, t) s`` β-reduces to ``t[s/x]``, that is, the result of replacing ``x`` by ``s`` in ``t``.
* *ζ-reduction* : An expression ``let x := s in t`` ζ-reduces to ``t[s/x]``. * *ζ-reduction* : An expression ``let x := s in t`` ζ-reduces to ``t[s/x]``.
* *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to ``t``. * *δ-reduction* : If ``c`` is a defined constant with definition ``t``, then ``c`` δ-reduces to to ``t``.
* *ι-reduction* : When a function defined by recursion on an inductive type is applied to an element given by an explicit constructor, the result ι-reduces to the specified function value, as described in [Inductive Types](inductive.md). * *ι-reduction* : When a function defined by recursion on an inductive type is applied to an element given by an explicit constructor, the result ι-reduces to the specified function value, as described in [Inductive Types](inductive.md).
The reduction relation is transitive, which is to say, is ``s`` reduces to ``s'`` and ``t`` reduces to ``t'``, then ``s t`` reduces to ``s' t'``, ``λ x, s`` reduces to ``λ x, s'``, and so on. If ``s`` and ``t`` reduce to a common term, they are said to be *definitionally equal*. Definitional equality is defined to be the smallest equivalence relation that satisfies all these properties and also includes α-equivalence and the following two relations: The reduction relation is transitive, which is to say, is ``s`` reduces to ``s'`` and ``t`` reduces to ``t'``, then ``s t`` reduces to ``s' t'``, ``λ x, s`` reduces to ``λ x, s'``, and so on. If ``s`` and ``t`` reduce to a common term, they are said to be *definitionally equal*. Definitional equality is defined to be the smallest equivalence relation that satisfies all these properties and also includes α-equivalence and the following two relations:

138
doc/flake.lock generated
View File

@@ -18,15 +18,12 @@
} }
}, },
"flake-utils": { "flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1710146030, "lastModified": 1656928814,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "narHash": "sha256-RIFfgBuKz6Hp89yRr7+NR5tzIAbn52h8vT6vXkYjZoM=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "rev": "7e2a3b3dfd9af950a856d66b0a7d01e3c18aa249",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -38,12 +35,13 @@
"lean": { "lean": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs", "lean4-mode": "lean4-mode",
"nixpkgs-old": "nixpkgs-old" "nix": "nix",
"nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 0, "lastModified": 0,
"narHash": "sha256-saRAtQ6VautVXKDw1XH35qwP0KEBKTKZbg/TRa4N9Vw=", "narHash": "sha256-YnYbmG0oou1Q/GE4JbMNb8/yqUVXBPIvcdQQJHBqtPk=",
"path": "../.", "path": "../.",
"type": "path" "type": "path"
}, },
@@ -52,6 +50,22 @@
"type": "path" "type": "path"
} }
}, },
"lean4-mode": {
"flake": false,
"locked": {
"lastModified": 1659020985,
"narHash": "sha256-+dRaXB7uvN/weSZiKcfSKWhcdJVNg9Vg8k0pJkDNjpc=",
"owner": "leanprover",
"repo": "lean4-mode",
"rev": "37d5c99b7b29c80ab78321edd6773200deb0bca6",
"type": "github"
},
"original": {
"owner": "leanprover",
"repo": "lean4-mode",
"type": "github"
}
},
"leanInk": { "leanInk": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -69,6 +83,22 @@
"type": "github" "type": "github"
} }
}, },
"lowdown-src": {
"flake": false,
"locked": {
"lastModified": 1633514407,
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
"owner": "kristapsdz",
"repo": "lowdown",
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
"type": "github"
},
"original": {
"owner": "kristapsdz",
"repo": "lowdown",
"type": "github"
}
},
"mdBook": { "mdBook": {
"flake": false, "flake": false,
"locked": { "locked": {
@@ -85,13 +115,65 @@
"type": "github" "type": "github"
} }
}, },
"nix": {
"inputs": {
"lowdown-src": "lowdown-src",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1657097207,
"narHash": "sha256-SmeGmjWM3fEed3kQjqIAO8VpGmkC2sL1aPE7kKpK650=",
"owner": "NixOS",
"repo": "nix",
"rev": "f6316b49a0c37172bca87ede6ea8144d7d89832f",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1710889954, "lastModified": 1653988320,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=", "narHash": "sha256-ZaqFFsSDipZ6KVqriwM34T739+KLYJvNmCWzErjAg7c=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b", "rev": "2fa57ed190fd6c7c746319444f34b5917666e5c1",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-22.05-small",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1657208011,
"narHash": "sha256-BlIFwopAykvdy1DYayEkj6ZZdkn+cVgPNX98QVLc0jM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "2770cc0b1e8faa0e20eb2c6aea64c256a706d4f2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -101,23 +183,6 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-old": {
"flake": false,
"locked": {
"lastModified": 1581379743,
"narHash": "sha256-i1XCn9rKuLjvCdu2UeXKzGLF6IuQePQKFt4hEKRU5oc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "34c7eb7545d155cc5b6f499b23a7cb1c96ab4d59",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-19.03",
"repo": "nixpkgs",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"alectryon": "alectryon", "alectryon": "alectryon",
@@ -129,21 +194,6 @@
"leanInk": "leanInk", "leanInk": "leanInk",
"mdBook": "mdBook" "mdBook": "mdBook"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View File

@@ -17,7 +17,7 @@
}; };
outputs = inputs@{ self, ... }: inputs.flake-utils.lib.eachDefaultSystem (system: outputs = inputs@{ self, ... }: inputs.flake-utils.lib.eachDefaultSystem (system:
with inputs.lean.packages.${system}.deprecated; with nixpkgs; with inputs.lean.packages.${system}; with nixpkgs;
let let
doc-src = lib.sourceByRegex ../. ["doc.*" "tests(/lean(/beginEndAsMacro.lean)?)?"]; doc-src = lib.sourceByRegex ../. ["doc.*" "tests(/lean(/beginEndAsMacro.lean)?)?"];
in { in {
@@ -44,6 +44,21 @@
mdbook build -d $out mdbook build -d $out
''; '';
}; };
# We use a separate derivation instead of `checkPhase` so we can push it but not `doc` to the binary cache
test = stdenv.mkDerivation {
name ="lean-doc-test";
src = doc-src;
buildInputs = [ lean-mdbook stage1.Lean.lean-package strace ];
patchPhase = ''
cd doc
patchShebangs test
'';
buildPhase = ''
mdbook test
touch $out
'';
dontInstall = true;
};
leanInk = (buildLeanPackage { leanInk = (buildLeanPackage {
name = "Main"; name = "Main";
src = inputs.leanInk; src = inputs.leanInk;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

@@ -13,7 +13,7 @@ Recall that nonnegative numerals are considered to be a `Nat` if there are no ty
The operator `/` for `Int` implements integer division. The operator `/` for `Int` implements integer division.
```lean ```lean
#eval -10 / 4 -- -3 #eval -10 / 4 -- -2
``` ```
Similar to `Nat`, the internal representation of `Int` is optimized. Small integers are Similar to `Nat`, the internal representation of `Int` is optimized. Small integers are

100
doc/latex/lean4.py Normal file
View File

@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""
pygments.lexers.theorem
~~~~~~~~~~~~~~~~~~~~~~~
Lexers for theorem-proving languages.
:copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
from pygments.lexer import RegexLexer, default, words
from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
Number, Punctuation, Generic
__all__ = ['Lean4Lexer']
class Lean4Lexer(RegexLexer):
"""
For the `Lean 4 <https://github.com/leanprover/lean4>`_
theorem prover.
.. versionadded:: 2.0
"""
name = 'Lean4'
aliases = ['lean4']
filenames = ['*.lean']
mimetypes = ['text/x-lean']
flags = re.MULTILINE | re.UNICODE
keywords1 = (
'import', 'abbreviation', 'opaque_hint', 'tactic_hint', 'definition',
'renaming', 'inline', 'hiding', 'parameter', 'lemma', 'variable',
'theorem', 'axiom', 'inductive', 'structure', 'universe', 'alias',
'help', 'options', 'precedence', 'postfix', 'prefix',
'infix', 'infixl', 'infixr', 'notation', '#eval',
'#check', '#reduce', '#exit', 'coercion', 'end', 'private', 'using', 'namespace',
'including', 'instance', 'section', 'context', 'protected', 'expose',
'export', 'set_option', 'extends', 'open', 'example',
'constant', 'constants', 'print', 'opaque', 'reducible', 'irreducible',
'def', 'macro', 'elab', 'syntax', 'macro_rules', 'reduce', 'where',
'abbrev', 'noncomputable', 'class', 'attribute', 'synth', 'mutual',
)
keywords2 = (
'forall', 'fun', 'Pi', 'obtain', 'from', 'have', 'show', 'assume',
'take', 'let', 'if', 'else', 'then', 'by', 'in', 'with', 'begin',
'proof', 'qed', 'calc', 'match', 'nomatch', 'do', 'at',
)
keywords3 = (
# Sorts
'Type', 'Prop', 'Sort',
)
operators = (
u'!=', u'#', u'&', u'&&', u'*', u'+', u'-', u'/', u'@', u'!', u'`',
u'-.', u'->', u'.', u'..', u'...', u'::', u':>', u';', u';;', u'<',
u'<-', u'=', u'==', u'>', u'_', u'|', u'||', u'~', u'=>', u'<=', u'>=',
u'/\\', u'\\/', u'', u'Π', u'λ', u'', u'', u'', u'', u'', u'',
u'¬', u'⁻¹', u'', u'', u'', u'', u'', u'', u'', u'×', u'',
u'', u'', u'', u'',
)
punctuation = (u'(', u')', u':', u'{', u'}', u'[', u']', u'', u'',
u':=', u',')
tokens = {
'root': [
(r'\s+', Text),
(r'/-', Comment, 'comment'),
(r'--.*?$', Comment.Single),
(words(keywords1, prefix=r'\b', suffix=r'\b'), Keyword.Namespace),
(words(keywords2, prefix=r'\b', suffix=r'\b'), Keyword),
(words(keywords3, prefix=r'\b', suffix=r'\b'), Keyword.Type),
(words(operators), Name.Builtin.Pseudo),
(words(punctuation), Operator),
(u"[A-Za-z_\u03b1-\u03ba\u03bc-\u03fb\u1f00-\u1ffe\u2100-\u214f]"
u"[A-Za-z_'\u03b1-\u03ba\u03bc-\u03fb\u1f00-\u1ffe\u2070-\u2079"
u"\u207f-\u2089\u2090-\u209c\u2100-\u214f0-9]*", Name),
(r'\d+', Number.Integer),
(r'"', String.Double, 'string'),
(r'[~?][a-z][\w\']*:', Name.Variable)
],
'comment': [
# Multiline Comments
(r'[^/-]', Comment.Multiline),
(r'/-', Comment.Multiline, '#push'),
(r'-/', Comment.Multiline, '#pop'),
(r'[/-]', Comment.Multiline)
],
'string': [
(r'[^\\"]+', String.Double),
(r'\\[n"\\]', String.Escape),
('"', String.Double, '#pop'),
],
}

View File

@@ -128,16 +128,16 @@ Numeric literals can be specified in various bases.
``` ```
numeral : numeral10 | numeral2 | numeral8 | numeral16 numeral : numeral10 | numeral2 | numeral8 | numeral16
numeral10 : [0-9]+ ("_"+ [0-9]+)* numeral10 : [0-9]+
numeral2 : "0" [bB] ("_"* [0-1]+)+ numeral2 : "0" [bB] [0-1]+
numeral8 : "0" [oO] ("_"* [0-7]+)+ numeral8 : "0" [oO] [0-7]+
numeral16 : "0" [xX] ("_"* hex_char+)+ numeral16 : "0" [xX] hex_char+
``` ```
Floating point literals are also possible with optional exponent: Floating point literals are also possible with optional exponent:
``` ```
float : numeral10 "." numeral10? [eE[+-]numeral10] float : [0-9]+ "." [0-9]+ [[eE[+-][0-9]+]
``` ```
For example: For example:
@@ -147,7 +147,6 @@ constant w : Int := 55
constant x : Nat := 26085 constant x : Nat := 26085
constant y : Nat := 0x65E5 constant y : Nat := 0x65E5
constant z : Float := 2.548123e-05 constant z : Float := 2.548123e-05
constant b : Bool := 0b_11_01_10_00
``` ```
Note: that negative numbers are created by applying the "-" negation prefix operator to the number, for example: Note: that negative numbers are created by applying the "-" negation prefix operator to the number, for example:

View File

@@ -1,14 +1,9 @@
These are instructions to set up a working development environment for those who wish to make changes to Lean itself. It is part of the [Development Guide](../dev/index.md).
We strongly suggest that new users instead follow the [Quickstart](../quickstart.md) to get started using Lean, since this sets up an environment that can automatically manage multiple Lean toolchain versions, which is necessary when working within the Lean ecosystem.
Requirements Requirements
------------ ------------
- C++14 compatible compiler - C++14 compatible compiler
- [CMake](http://www.cmake.org) - [CMake](http://www.cmake.org)
- [GMP (GNU multiprecision library)](http://gmplib.org/) - [GMP (GNU multiprecision library)](http://gmplib.org/)
- [LibUV](https://libuv.org/)
Platform-Specific Setup Platform-Specific Setup
----------------------- -----------------------
@@ -22,27 +17,39 @@ Platform-Specific Setup
Generic Build Instructions Generic Build Instructions
-------------------------- --------------------------
Setting up a basic parallelized release build: Setting up a basic release build:
```bash ```bash
git clone https://github.com/leanprover/lean4 git clone https://github.com/leanprover/lean4 --recurse-submodules
cd lean4 cd lean4
cmake --preset release mkdir -p build/release
make -C build/release -j$(nproc || sysctl -n hw.logicalcpu) cd build/release
cmake ../..
make
``` ```
You can replace `$(nproc || sysctl -n hw.logicalcpu)` with the desired parallelism amount.
For regular development, we recommend running
```bash
git config submodule.recurse true
```
in the checkout so that `--recurse-submodules` doesn't have to be
specified with `git pull/checkout/...`.
The above commands will compile the Lean library and binaries into the The above commands will compile the Lean library and binaries into the
`stage1` subfolder; see below for details. `stage1` subfolder; see below for details. Add `-j N` for an
appropriate `N` to `make` for a parallel build.
You should not usually run `cmake --install` after a successful build. For example, on an AMD Ryzen 9 `make` takes 00:04:55, whereas `make -j 10`
takes 00:01:38. Your results may vary depending on the speed of your hard
drive.
You should not usually run `make install` after a successful build.
See [Dev setup using elan](../dev/index.md#dev-setup-using-elan) on how to properly set up your editor to use the correct stage depending on the source directory. See [Dev setup using elan](../dev/index.md#dev-setup-using-elan) on how to properly set up your editor to use the correct stage depending on the source directory.
Useful CMake Configuration Settings Useful CMake Configuration Settings
----------------------------------- -----------------------------------
Pass these along with the `cmake --preset release` command. Pass these along with the `cmake ../..` command.
There are also two alternative presets that combine some of these options you can use instead of `release`: `debug` and `sandebug` (sanitize + debug).
* `-D CMAKE_BUILD_TYPE=`\ * `-D CMAKE_BUILD_TYPE=`\
Select the build type. Valid values are `RELEASE` (default), `DEBUG`, Select the build type. Valid values are `RELEASE` (default), `DEBUG`,

39
doc/make/msvc.md Normal file
View File

@@ -0,0 +1,39 @@
# Compiling Lean with Visual Studio
WARNING: Compiling Lean with Visual Studio doesn't currently work.
There's an ongoing effort to port Lean to Visual Studio.
The instructions below are for VS 2017.
In the meantime you can use [MSYS2](msys2.md) or [WSL](wsl.md).
## Installing dependencies
First, install `vcpkg` from https://github.com/Microsoft/vcpkg if you haven't
done so already.
Then, open a console in the directory you cloned `vcpkg` to, and type:
`vcpkg install mpir` for the 32-bit library or
`vcpkg install mpir:x64-windows` for the x64 one.
In Visual Studio, use the "open folder" feature and open the Lean directory.
Go to the `CMake->Change CMake Settings` menu. File `CMakeSettings.json` opens.
In each of the targets, add the following snippet (i.e., after every
`ctestCommandArgs`):
```json
"variables": [
{
"name": "CMAKE_TOOLCHAIN_FILE",
"value": "C:\\path\\to\\vcpkg\\scripts\\buildsystems\\vcpkg.cmake"
}
]
```
## Enable Intellisense
In Visual Studio, press Ctrl+Q and type `CppProperties.json` and press Enter.
Ensure `includePath` variables include `"${workspaceRoot}\\src"`.
## Build Lean
Press F7.

View File

@@ -15,24 +15,17 @@ Mode](https://docs.microsoft.com/en-us/windows/apps/get-started/enable-your-devi
which will allow Lean to create symlinks that e.g. enable go-to-definition in which will allow Lean to create symlinks that e.g. enable go-to-definition in
the stdlib. the stdlib.
## Installing the Windows SDK
Install the Windows SDK from [Microsoft](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/).
The oldest supported version is 10.0.18362.0. If you installed the Windows SDK to the default location,
then there should be a directory with the version number at `C:\Program Files (x86)\Windows Kits\10\Include`.
If there are multiple directories, only the highest version number matters.
## Installing dependencies ## Installing dependencies
[The official webpage of MSYS2][msys2] provides one-click installers. [The official webpage of MSYS2][msys2] provides one-click installers.
Once installed, you should run the "MSYS2 CLANG64" shell from the start menu (the one that runs `clang64.exe`). Once installed, you should run the "MSYS2 MinGW 64-bit shell" from the start menu (the one that runs `mingw64.exe`).
Do not run "MSYS2 MSYS" or "MSYS2 MINGW64" instead! Do not run "MSYS2 MSYS" instead!
MSYS2 has a package management system, [pacman][pacman]. MSYS2 has a package management system, [pacman][pacman], which is used in Arch Linux.
Here are the commands to install all dependencies needed to compile Lean on your machine. Here are the commands to install all dependencies needed to compile Lean on your machine.
```bash ```bash
pacman -S make python mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-clang mingw-w64-clang-x86_64-ccache mingw-w64-clang-x86_64-libuv mingw-w64-clang-x86_64-gmp git unzip diffutils binutils pacman -S make python mingw-w64-x86_64-cmake mingw-w64-x86_64-clang mingw-w64-x86_64-ccache git unzip diffutils binutils
``` ```
You should now be able to run these commands: You should now be able to run these commands:
@@ -45,9 +38,10 @@ cmake --version
Then follow the [generic build instructions](index.md) in the MSYS2 Then follow the [generic build instructions](index.md) in the MSYS2
MinGW shell, using: MinGW shell, using:
``` ```
cmake --preset release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ cmake ../.. -G "Unix Makefiles" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
``` ```
instead of `cmake --preset release`. This will use the clang compiler instead of gcc, which is required with msys2. instead of `cmake ../..`. This ensures that cmake will call `sh` instead of `cmd.exe`
for script tasks and it will use the clang compiler instead of gcc, which is required.
## Install lean ## Install lean
@@ -68,9 +62,9 @@ If you want a version that can run independently of your MSYS install
then you need to copy the following dependent DLL's from where ever then you need to copy the following dependent DLL's from where ever
they are installed in your MSYS setup: they are installed in your MSYS setup:
- libc++.dll - libgcc_s_seh-1.dll
- libstdc++-6.dll
- libgmp-10.dll - libgmp-10.dll
- libuv-1.dll
- libwinpthread-1.dll - libwinpthread-1.dll
The following linux command will do that: The following linux command will do that:
@@ -88,6 +82,6 @@ version clang to your path.
**-bash: gcc: command not found** **-bash: gcc: command not found**
Make sure `/clang64/bin` is in your PATH environment. If it is not then Make sure `/mingw64/bin` is in your PATH environment. If it is not then
check you launched the MSYS2 CLANG64 shell from the start menu. check you launched the MSYS2 MinGW 64-bit shell from the start menu.
(The one that runs `clang64.exe`). (The one that runs `mingw64.exe`).

View File

@@ -1,4 +1,4 @@
# Install Packages on OS X 14.5 # Install Packages on OS X 10.9
We assume that you are using [homebrew][homebrew] as a package manager. We assume that you are using [homebrew][homebrew] as a package manager.
@@ -22,7 +22,7 @@ brew install gcc
``` ```
To install clang++-3.5 via homebrew, please execute: To install clang++-3.5 via homebrew, please execute:
```bash ```bash
brew install llvm brew install llvm --with-clang --with-asan
``` ```
To use compilers other than the default one (Apple's clang++), you To use compilers other than the default one (Apple's clang++), you
need to use `-DCMAKE_CXX_COMPILER` option to specify the compiler need to use `-DCMAKE_CXX_COMPILER` option to specify the compiler
@@ -32,16 +32,15 @@ following to use `g++`.
cmake -DCMAKE_CXX_COMPILER=g++ ... cmake -DCMAKE_CXX_COMPILER=g++ ...
``` ```
## Required Packages: CMake, GMP, libuv ## Required Packages: CMake, GMP
```bash ```bash
brew install cmake brew install cmake
brew install gmp brew install gmp
brew install libuv
``` ```
## Recommended Packages: CCache ## Recommended Packages: CCache
```bash ```bash
brew install ccache brew install ccache
``` ```

View File

@@ -8,5 +8,5 @@ follow the [generic build instructions](index.md).
## Basic packages ## Basic packages
```bash ```bash
sudo apt-get install git libgmp-dev libuv1-dev cmake ccache clang sudo apt-get install git libgmp-dev cmake ccache clang
``` ```

View File

@@ -138,8 +138,8 @@ definition:
-/ -/
instance : Applicative List where instance : Applicative List where
pure := List.singleton pure := List.pure
seq f x := List.flatMap f fun y => Functor.map y (x ()) seq f x := List.bind f fun y => Functor.map y (x ())
/-! /-!
Notice you can now sequence a _list_ of functions and a _list_ of items. Notice you can now sequence a _list_ of functions and a _list_ of items.

View File

@@ -128,8 +128,8 @@ Applying the identity function through an applicative structure should not chang
values or structure. For example: values or structure. For example:
-/ -/
instance : Applicative List where instance : Applicative List where
pure := List.singleton pure := List.pure
seq f x := List.flatMap f fun y => Functor.map y (x ()) seq f x := List.bind f fun y => Functor.map y (x ())
#eval pure id <*> [1, 2, 3] -- [1, 2, 3] #eval pure id <*> [1, 2, 3] -- [1, 2, 3]
/-! /-!
@@ -171,7 +171,7 @@ of data contained in the container resulting in a new container that has the sam
`u <*> pure y = pure (. y) <*> u`. `u <*> pure y = pure (. y) <*> u`.
This law is a little more complicated, so don't sweat it too much. It states that the order that This law is is a little more complicated, so don't sweat it too much. It states that the order that
you wrap things shouldn't matter. One the left, you apply any applicative `u` over a pure wrapped you wrap things shouldn't matter. One the left, you apply any applicative `u` over a pure wrapped
object. On the right, you first wrap a function applying the object as an argument. Note that `(· object. On the right, you first wrap a function applying the object as an argument. Note that `(·
y)` is short hand for: `fun f => f y`. Then you apply this to the first applicative `u`. These y)` is short hand for: `fun f => f y`. Then you apply this to the first applicative `u`. These
@@ -235,8 +235,8 @@ structure or its values.
Left identity is `x >>= pure = x` and is demonstrated by the following examples on a monadic `List`: Left identity is `x >>= pure = x` and is demonstrated by the following examples on a monadic `List`:
-/ -/
instance : Monad List where instance : Monad List where
pure := List.singleton pure := List.pure
bind := List.flatMap bind := List.bind
def a := ["apple", "orange"] def a := ["apple", "orange"]

View File

@@ -192,8 +192,8 @@ implementation of `pure` and `bind`.
-/ -/
instance : Monad List where instance : Monad List where
pure := List.singleton pure := List.pure
bind := List.flatMap bind := List.bind
/-! /-!
Like you saw with the applicative `seq` operator, the `bind` operator applies the given function Like you saw with the applicative `seq` operator, the `bind` operator applies the given function

View File

@@ -139,7 +139,7 @@ You might be wondering, how does the context actually move through the `ReaderM`
add an input argument to a function by modifying its return type? There is a special command in add an input argument to a function by modifying its return type? There is a special command in
Lean that will show you the reduced types: Lean that will show you the reduced types:
-/ -/
#reduce (types := true) ReaderM Environment String -- Environment → String #reduce ReaderM Environment String -- Environment → String
/-! /-!
And you can see here that this type is actually a function! It's a function that takes an And you can see here that this type is actually a function! It's a function that takes an
`Environment` as input and returns a `String`. `Environment` as input and returns a `String`.
@@ -196,4 +196,4 @@ entirely.
Now it's time to move on to [StateM Monad](states.lean.md) which is like a `ReaderM` that is Now it's time to move on to [StateM Monad](states.lean.md) which is like a `ReaderM` that is
also updatable. also updatable.
-/ -/

View File

@@ -5,19 +5,14 @@ See [Setup](./setup.md) for supported platforms and other ways to set up Lean 4.
1. Install [VS Code](https://code.visualstudio.com/). 1. Install [VS Code](https://code.visualstudio.com/).
1. Launch VS Code and install the `Lean 4` extension by clicking on the 'Extensions' sidebar entry and searching for 'Lean 4'. 1. Launch VS Code and install the `lean4` extension by clicking on the "Extensions" sidebar entry and searching for "lean4".
![installing the vscode-lean4 extension](images/code-ext.png) ![installing the vscode-lean4 extension](images/code-ext.png)
1. Open the Lean 4 setup guide by creating a new text file using 'File > New Text File' (`Ctrl+N` / `Cmd+N`), clicking on the ∀-symbol in the top right and selecting 'Documentation… > Docs: Show Setup Guide'. 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".
![show setup guide](images/show-setup-guide.png) ![show setup guide](images/show-setup-guide.png)
1. Follow the Lean 4 setup guide. It will: 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.
- walk you through learning resources for Lean, ![setup guide](images/setup_guide.png)
- teach you how to set up Lean's dependencies on your platform,
- install Lean 4 for you at the click of a button,
- help you set up your first project.
![setup guide](images/setup_guide.png)

View File

@@ -6,8 +6,7 @@ Platforms built & tested by our CI, available as binary releases via elan (see b
* x86-64 Linux with glibc 2.27+ * x86-64 Linux with glibc 2.27+
* x86-64 macOS 10.15+ * x86-64 macOS 10.15+
* aarch64 (Apple Silicon) macOS 10.15+ * x86-64 Windows 10+
* x86-64 Windows 11 (any version), Windows 10 (version 1903 or higher), Windows Server 2022
### Tier 2 ### Tier 2
@@ -17,6 +16,7 @@ Releases may be silently broken due to the lack of automated testing.
Issue reports and fixes are welcome. Issue reports and fixes are welcome.
* aarch64 Linux with glibc 2.27+ * aarch64 Linux with glibc 2.27+
* aarch64 (Apple Silicon) macOS
* x86 (32-bit) Linux * x86 (32-bit) Linux
* Emscripten Web Assembly * Emscripten Web Assembly

View File

@@ -43,8 +43,7 @@ $ pdflatex test.tex
## Example with `minted` ## Example with `minted`
First [install Pygments](https://pygments.org/download/) (version 2.18 or newer). First [install Pygments](https://pygments.org/download/). Then save [`lean4.py`](https://raw.githubusercontent.com/leanprover/lean4/master/doc/latex/lean4.py), which contains an version of the Lean highlighter updated for Lean 4, and the following sample LaTeX file `test.tex` into the same directory:
Then save the following sample LaTeX file `test.tex` into the same directory:
```latex ```latex
\documentclass{article} \documentclass{article}
@@ -52,8 +51,9 @@ Then save the following sample LaTeX file `test.tex` into the same directory:
% switch to a monospace font supporting more Unicode characters % switch to a monospace font supporting more Unicode characters
\setmonofont{FreeMono} \setmonofont{FreeMono}
\usepackage{minted} \usepackage{minted}
\newmintinline[lean]{lean4}{bgcolor=white} % instruct minted to use our local theorem.py
\newminted[leancode]{lean4}{fontsize=\footnotesize} \newmintinline[lean]{lean4.py:Lean4Lexer -x}{bgcolor=white}
\newminted[leancode]{lean4.py:Lean4Lexer -x}{fontsize=\footnotesize}
\usemintedstyle{tango} % a nice, colorful theme \usemintedstyle{tango} % a nice, colorful theme
\begin{document} \begin{document}
@@ -67,6 +67,9 @@ theorem funext {f₁ f₂ : ∀ (x : α), β x} (h : ∀ x, f₁ x = f₂ x) : f
\end{document} \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: You can then compile `test.tex` by executing the following command:
```bash ```bash
@@ -78,14 +81,11 @@ Some remarks:
- either `xelatex` or `lualatex` is required to handle Unicode characters in the code. - either `xelatex` or `lualatex` is required to handle Unicode characters in the code.
- `--shell-escape` is needed to allow `xelatex` to execute `pygmentize` in a shell. - `--shell-escape` is needed to allow `xelatex` to execute `pygmentize` in a shell.
- If the chosen monospace font is missing some Unicode symbols, you can direct them to be displayed using a fallback font or other replacement LaTeX code. - If the chosen monospace font is missing some Unicode symbols, you can direct them to be displayed using a fallback font or other replacement LaTeX code.
``` latex ``` latex
\usepackage{newunicodechar} \usepackage{newunicodechar}
\newfontfamily{\freeserif}{DejaVu Sans} \newfontfamily{\freeserif}{DejaVu Sans}
\newunicodechar{✝}{\freeserif{✝}} \newunicodechar{✝}{\freeserif{✝}}
\newunicodechar{𝓞}{\ensuremath{\mathcal{O}}} \newunicodechar{𝓞}{\ensuremath{\mathcal{O}}}
``` ```
- If you are using an old version of Pygments, you can copy - minted has a "helpful" feature that draws red boxes around characters the chosen lexer doesn't recognize.
[`lean.py`](https://raw.githubusercontent.com/pygments/pygments/master/pygments/lexers/lean.py) into your working directory, Since the Lean lexer cannot encompass all user-defined syntax, it is advisable to [work around](https://tex.stackexchange.com/a/343506/14563) this feature.
and use `lean4.py:Lean4Lexer -x` instead of `lean4` above.
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.

120
flake.lock generated
View File

@@ -1,5 +1,21 @@
{ {
"nodes": { "nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1673956053,
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
@@ -18,35 +34,72 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "lean4-mode": {
"flake": false,
"locked": { "locked": {
"lastModified": 1710889954, "lastModified": 1709737301,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=", "narHash": "sha256-uT9JN2kLNKJK9c/S/WxLjiHmwijq49EgLb+gJUSDpz0=",
"owner": "NixOS", "owner": "leanprover",
"repo": "nixpkgs", "repo": "lean4-mode",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b", "rev": "f1f24c15134dee3754b82c9d9924866fe6bc6b9f",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "leanprover",
"ref": "nixpkgs-unstable", "repo": "lean4-mode",
"repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"nixpkgs-cadical": { "libgit2": {
"flake": false,
"locked": { "locked": {
"lastModified": 1722221733, "lastModified": 1697646580,
"narHash": "sha256-sga9SrrPb+pQJxG1ttJfMPheZvDOxApFfwXCFO0H9xw=", "narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": "flake-compat",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs",
"nixpkgs-regression": "nixpkgs-regression"
},
"locked": {
"lastModified": 1711102798,
"narHash": "sha256-CXOIJr8byjolqG7eqCLa+Wfi7rah62VmLoqSXENaZnw=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nix",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e", "rev": "a22328066416650471c3545b0b138669ea212ab4",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1709083642,
"narHash": "sha256-7kkJQd4rZ+vFrzWu8sTRtta5D1kBG0LSRYAfhtmMlSo=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "12bf09802d77264e441f48e25459c10c93eada2e", "rev": "b550fe4b4776908ac2a861124307045f8e717c8e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.11",
"repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
@@ -67,11 +120,44 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs-regression": {
"locked": {
"lastModified": 1643052045,
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1710889954,
"narHash": "sha256-Pr6F5Pmd7JnNEMHHmspZ0qVqIBVxyZ13ik1pJtm2QXk=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7872526e9c5332274ea5932a0c3270d6e4724f3b",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs", "lean4-mode": "lean4-mode",
"nixpkgs-cadical": "nixpkgs-cadical", "nix": "nix",
"nixpkgs": "nixpkgs_2",
"nixpkgs-old": "nixpkgs-old" "nixpkgs-old": "nixpkgs-old"
} }
}, },

125
flake.nix
View File

@@ -1,77 +1,96 @@
{ {
description = "Lean development flake. Not intended for end users."; description = "Lean interactive theorem prover";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# old nixpkgs used for portable release with older glibc (2.27) # old nixpkgs used for portable release with older glibc (2.27)
inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03"; inputs.nixpkgs-old.url = "github:NixOS/nixpkgs/nixos-19.03";
inputs.nixpkgs-old.flake = false; inputs.nixpkgs-old.flake = false;
# for cadical 1.9.5; sync with CMakeLists.txt
inputs.nixpkgs-cadical.url = "github:NixOS/nixpkgs/12bf09802d77264e441f48e25459c10c93eada2e";
inputs.flake-utils.url = "github:numtide/flake-utils"; inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.nix.url = "github:NixOS/nix";
inputs.lean4-mode = {
url = "github:leanprover/lean4-mode";
flake = false;
};
# used *only* by `stage0-from-input` below
#inputs.lean-stage0 = {
# url = github:leanprover/lean4;
# inputs.nixpkgs.follows = "nixpkgs";
# inputs.flake-utils.follows = "flake-utils";
# inputs.nix.follows = "nix";
# inputs.lean4-mode.follows = "lean4-mode";
#};
outputs = { self, nixpkgs, nixpkgs-old, flake-utils, ... }@inputs: flake-utils.lib.eachDefaultSystem (system: outputs = { self, nixpkgs, nixpkgs-old, flake-utils, nix, lean4-mode, ... }@inputs: flake-utils.lib.eachDefaultSystem (system:
let let
pkgs = import nixpkgs { inherit system; }; pkgs = import nixpkgs {
inherit system;
# for `vscode-with-extensions`
config.allowUnfree = true;
};
# An old nixpkgs for creating releases with an old glibc # An old nixpkgs for creating releases with an old glibc
pkgsDist-old = import nixpkgs-old { inherit system; }; pkgsDist-old = import nixpkgs-old { inherit system; };
# An old nixpkgs for creating releases with an old glibc # An old nixpkgs for creating releases with an old glibc
pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; }; pkgsDist-old-aarch = import nixpkgs-old { localSystem.config = "aarch64-unknown-linux-gnu"; };
pkgsCadical = import inputs.nixpkgs-cadical { inherit system; };
cadical = if pkgs.stdenv.isLinux then
# use statically-linked cadical on Linux to avoid glibc versioning troubles
pkgsCadical.pkgsStatic.cadical.overrideAttrs { doCheck = false; }
else pkgsCadical.cadical;
lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; }; lean-packages = pkgs.callPackage (./nix/packages.nix) { src = ./.; inherit nix lean4-mode; };
devShellWithDist = pkgsDist: pkgs.mkShell.override { devShellWithDist = pkgsDist: pkgs.mkShell.override {
stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang; stdenv = pkgs.overrideCC pkgs.stdenv lean-packages.llvmPackages.clang;
} ({ } ({
buildInputs = with pkgs; [ buildInputs = with pkgs; [
cmake gmp libuv ccache cadical cmake gmp ccache
lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan lean-packages.llvmPackages.llvm # llvm-symbolizer for asan/lsan
gdb # TODO: only add when proven to not affect the flakification
tree # for CI #pkgs.python3
]; ];
# https://github.com/NixOS/nixpkgs/issues/60919 # https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ]; hardeningDisable = [ "all" ];
# more convenient `ctest` output # more convenient `ctest` output
CTEST_OUTPUT_ON_FAILURE = 1; CTEST_OUTPUT_ON_FAILURE = 1;
} // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux { } // pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
GMP = (pkgsDist.gmp.override { withStatic = true; }).overrideAttrs (attrs: GMP = pkgsDist.gmp.override { withStatic = true; };
pkgs.lib.optionalAttrs (pkgs.stdenv.system == "aarch64-linux") { GLIBC = pkgsDist.glibc;
# would need additional linking setup on Linux aarch64, we don't use it anywhere else either GLIBC_DEV = pkgsDist.glibc.dev;
hardeningDisable = [ "stackprotector" ]; GCC_LIB = pkgsDist.gcc.cc.lib;
}); ZLIB = pkgsDist.zlib;
LIBUV = pkgsDist.libuv.overrideAttrs (attrs: { GDB = pkgsDist.gdb;
configureFlags = ["--enable-static"]; });
hardeningDisable = [ "stackprotector" ];
# Sync version with CMakeLists.txt
version = "1.48.0";
src = pkgs.fetchFromGitHub {
owner = "libuv";
repo = "libuv";
rev = "v1.48.0";
sha256 = "100nj16fg8922qg4m2hdjh62zv4p32wyrllsvqr659hdhjc03bsk";
};
doCheck = false;
});
GLIBC = pkgsDist.glibc;
GLIBC_DEV = pkgsDist.glibc.dev;
GCC_LIB = pkgsDist.gcc.cc.lib;
ZLIB = pkgsDist.zlib;
GDB = pkgsDist.gdb;
});
in { in {
packages = { packages = lean-packages // rec {
# to be removed when Nix CI is not needed anymore debug = lean-packages.override { debug = true; };
inherit (lean-packages) cacheRoots test update-stage0-commit ciShell; stage0debug = lean-packages.override { stage0debug = true; };
deprecated = lean-packages; asan = lean-packages.override { extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=address" "-DLEANC_EXTRA_FLAGS=-fsanitize=address" "-DSMALL_ALLOCATOR=OFF" "-DSYMBOLIC=OFF" ]; };
asandebug = asan.override { debug = true; };
tsan = lean-packages.override {
extraCMakeFlags = [ "-DLEAN_EXTRA_CXX_FLAGS=-fsanitize=thread" "-DLEANC_EXTRA_FLAGS=-fsanitize=thread" "-DCOMPRESSED_OBJECT_HEADER=OFF" ];
stage0 = (lean-packages.override {
# Compressed headers currently trigger data race reports in tsan.
# Turn them off for stage 0 as well so stage 1 can read its own stdlib.
extraCMakeFlags = [ "-DCOMPRESSED_OBJECT_HEADER=OFF" ];
}).stage1;
};
tsandebug = tsan.override { debug = true; };
stage0-from-input = lean-packages.override {
stage0 = pkgs.writeShellScriptBin "lean" ''
exec ${inputs.lean-stage0.packages.${system}.lean}/bin/lean -Dinterpreter.prefer_native=false "$@"
'';
};
inherit self;
}; };
defaultPackage = lean-packages.lean-all;
# The default development shell for working on lean itself # The default development shell for working on lean itself
devShells.default = devShellWithDist pkgs; devShells.default = devShellWithDist pkgs;
devShells.oldGlibc = devShellWithDist pkgsDist-old; devShells.oldGlibc = devShellWithDist pkgsDist-old;
devShells.oldGlibcAArch = devShellWithDist pkgsDist-old-aarch; devShells.oldGlibcAArch = devShellWithDist pkgsDist-old-aarch;
});
checks.lean = lean-packages.test;
}) // rec {
templates.pkg = {
path = ./nix/templates/pkg;
description = "A custom Lean package";
};
defaultTemplate = templates.pkg;
};
} }

View File

@@ -1,13 +1,13 @@
{ src, debug ? false, stage0debug ? false, extraCMakeFlags ? [], { src, debug ? false, stage0debug ? false, extraCMakeFlags ? [],
stdenv, lib, cmake, gmp, libuv, cadical, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs, stdenv, lib, cmake, gmp, git, gnumake, bash, buildLeanPackage, writeShellScriptBin, runCommand, symlinkJoin, lndir, perl, gnused, darwin, llvmPackages, linkFarmFromDrvs,
... } @ args: ... } @ args:
with builtins; with builtins;
lib.warn "The Nix-based build is deprecated" rec { rec {
inherit stdenv; inherit stdenv;
sourceByRegex = p: rs: lib.sourceByRegex p (map (r: "(/src/)?${r}") rs); sourceByRegex = p: rs: lib.sourceByRegex p (map (r: "(/src/)?${r}") rs);
buildCMake = args: stdenv.mkDerivation ({ buildCMake = args: stdenv.mkDerivation ({
nativeBuildInputs = [ cmake ]; nativeBuildInputs = [ cmake ];
buildInputs = [ gmp libuv llvmPackages.llvm ]; buildInputs = [ gmp llvmPackages.llvm ];
# https://github.com/NixOS/nixpkgs/issues/60919 # https://github.com/NixOS/nixpkgs/issues/60919
hardeningDisable = [ "all" ]; hardeningDisable = [ "all" ];
dontStrip = (args.debug or debug); dontStrip = (args.debug or debug);
@@ -17,7 +17,7 @@ lib.warn "The Nix-based build is deprecated" rec {
''; '';
} // args // { } // args // {
src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]); src = args.realSrc or (sourceByRegex args.src [ "[a-z].*" "CMakeLists\.txt" ]);
cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" "-DCADICAL=${cadical}/bin/cadical" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ]; cmakeFlags = (args.cmakeFlags or [ "-DSTAGE=1" "-DPREV_STAGE=./faux-prev-stage" "-DUSE_GITHASH=OFF" ]) ++ (args.extraCMakeFlags or extraCMakeFlags) ++ lib.optional (args.debug or debug) [ "-DCMAKE_BUILD_TYPE=Debug" ];
preConfigure = args.preConfigure or "" + '' preConfigure = args.preConfigure or "" + ''
# ignore absence of submodule # ignore absence of submodule
sed -i 's!lake/Lake.lean!!' CMakeLists.txt sed -i 's!lake/Lake.lean!!' CMakeLists.txt
@@ -26,7 +26,11 @@ lib.warn "The Nix-based build is deprecated" rec {
lean-bin-tools-unwrapped = buildCMake { lean-bin-tools-unwrapped = buildCMake {
name = "lean-bin-tools"; name = "lean-bin-tools";
outputs = [ "out" "leanc_src" ]; outputs = [ "out" "leanc_src" ];
realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "[a-z].*" ".*\.in" "Leanc\.lean" ]; realSrc = sourceByRegex (src + "/src") [ "CMakeLists\.txt" "cmake.*" "bin.*" "include.*" ".*\.in" "Leanc\.lean" ];
preConfigure = ''
touch empty.cpp
sed -i 's/add_subdirectory.*//;s/set(LEAN_OBJS.*/set(LEAN_OBJS empty.cpp)/' CMakeLists.txt
'';
dontBuild = true; dontBuild = true;
installPhase = '' installPhase = ''
mkdir $out $leanc_src mkdir $out $leanc_src
@@ -41,10 +45,11 @@ lib.warn "The Nix-based build is deprecated" rec {
leancpp = buildCMake { leancpp = buildCMake {
name = "leancpp"; name = "leancpp";
src = src + "/src"; src = src + "/src";
buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "leanshell" "leanmain" ]; buildFlags = [ "leancpp" "leanrt" "leanrt_initial-exec" "shell" ];
installPhase = '' installPhase = ''
mkdir -p $out mkdir -p $out
mv lib/ $out/ mv lib/ $out/
mv shell/CMakeFiles/shell.dir/lean.cpp.o $out/lib
mv runtime/libleanrt_initial-exec.a $out/lib mv runtime/libleanrt_initial-exec.a $out/lib
''; '';
}; };
@@ -82,8 +87,7 @@ lib.warn "The Nix-based build is deprecated" rec {
leanFlags = [ "-DwarningAsError=true" ]; leanFlags = [ "-DwarningAsError=true" ];
} // args); } // args);
Init' = build { name = "Init"; deps = []; }; Init' = build { name = "Init"; deps = []; };
Std' = build { name = "Std"; deps = [ Init' ]; }; Lean' = build { name = "Lean"; deps = [ Init' ]; };
Lean' = build { name = "Lean"; deps = [ Std' ]; };
attachSharedLib = sharedLib: pkg: pkg // { attachSharedLib = sharedLib: pkg: pkg // {
inherit sharedLib; inherit sharedLib;
mods = mapAttrs (_: m: m // { inherit sharedLib; propagatedLoadDynlibs = []; }) pkg.mods; mods = mapAttrs (_: m: m // { inherit sharedLib; propagatedLoadDynlibs = []; }) pkg.mods;
@@ -91,61 +95,55 @@ lib.warn "The Nix-based build is deprecated" rec {
in (all: all // all.lean) rec { in (all: all // all.lean) rec {
inherit (Lean) emacs-dev emacs-package vscode-dev vscode-package; inherit (Lean) emacs-dev emacs-package vscode-dev vscode-package;
Init = attachSharedLib leanshared Init'; Init = attachSharedLib leanshared Init';
Std = attachSharedLib leanshared Std' // { allExternalDeps = [ Init ]; }; Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Init ]; };
Lean = attachSharedLib leanshared Lean' // { allExternalDeps = [ Std ]; };
Lake = build { Lake = build {
name = "Lake"; name = "Lake";
sharedLibName = "Lake_shared";
src = src + "/src/lake"; src = src + "/src/lake";
deps = [ Init Lean ]; deps = [ Init Lean ];
}; };
Lake-Main = build { Lake-Main = build {
name = "LakeMain"; name = "Lake.Main";
roots = [{ glob = "one"; mod = "LakeMain"; }]; roots = [ "Lake.Main" ];
executableName = "lake"; executableName = "lake";
deps = [ Lake ]; deps = [ Lake ];
linkFlags = lib.optional stdenv.isLinux "-rdynamic"; linkFlags = lib.optional stdenv.isLinux "-rdynamic";
src = src + "/src/lake"; src = src + "/src/lake";
}; };
stdlib = [ Init Std Lean Lake ]; stdlib = [ Init Lean Lake ];
modDepsFiles = symlinkJoin { name = "modDepsFiles"; paths = map (l: l.modDepsFile) (stdlib ++ [ Leanc ]); }; modDepsFiles = symlinkJoin { name = "modDepsFiles"; paths = map (l: l.modDepsFile) (stdlib ++ [ Leanc ]); };
depRoots = symlinkJoin { name = "depRoots"; paths = map (l: l.depRoots) stdlib; }; depRoots = symlinkJoin { name = "depRoots"; paths = map (l: l.depRoots) stdlib; };
iTree = symlinkJoin { name = "ileans"; paths = map (l: l.iTree) stdlib; }; iTree = symlinkJoin { name = "ileans"; paths = map (l: l.iTree) stdlib; };
Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; roots = [ "Leanc" ]; }; Leanc = build { name = "Leanc"; src = lean-bin-tools-unwrapped.leanc_src; deps = stdlib; roots = [ "Leanc" ]; };
stdlibLinkFlags = "${lib.concatMapStringsSep " " (l: "-L${l.staticLib}") stdlib} -L${leancpp}/lib/lean"; stdlibLinkFlags = "-L${Init.staticLib} -L${Lean.staticLib} -L${Lake.staticLib} -L${leancpp}/lib/lean";
libInit_shared = runCommand "libInit_shared" { buildInputs = [ stdenv.cc ]; libName = "libInit_shared${stdenv.hostPlatform.extensions.sharedLibrary}"; } '' libInit_shared = runCommand "libInit_shared" { buildInputs = [ stdenv.cc ]; libName = "libInit_shared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out mkdir $out
touch empty.c LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c -Wl,--whole-archive -lInit ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
''; $(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
leanshared_1 = runCommand "leanshared_1" { buildInputs = [ stdenv.cc ]; libName = "leanshared_1${stdenv.hostPlatform.extensions.sharedLibrary}"; } '' -o $out/$libName
mkdir $out
touch empty.c
${stdenv.cc}/bin/cc -shared -o $out/$libName empty.c
''; '';
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } '' leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \ LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
-Wl,--whole-archive ${leancpp}/lib/temp/libleanshell.a -lInit -lStd -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++ \ ${libInit_shared}/* -Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
-lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \ $(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName -o $out/$libName
''; '';
mods = foldl' (mods: pkg: mods // pkg.mods) {} stdlib; mods = foldl' (mods: pkg: mods // pkg.mods) {} stdlib;
print-paths = Lean.makePrintPathsFor [] mods; print-paths = Lean.makePrintPathsFor [] mods;
leanc = writeShellScriptBin "leanc" '' leanc = writeShellScriptBin "leanc" ''
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared_1} -L${leanshared} -L${Lake.sharedLib} "$@" LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${libInit_shared} -L${leanshared} "$@"
''; '';
lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } '' lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } ''
mkdir -p $out/bin mkdir -p $out/bin
${leanc}/bin/leanc ${leancpp}/lib/temp/libleanmain.a ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* -o $out/bin/lean ${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${libInit_shared}/* ${leanshared}/* -o $out/bin/lean
''; '';
# derivation following the directory layout of the "basic" setup, mostly useful for running tests # derivation following the directory layout of the "basic" setup, mostly useful for running tests
lean-all = stdenv.mkDerivation { lean-all = stdenv.mkDerivation {
name = "lean-${desc}"; name = "lean-${desc}";
buildCommand = '' buildCommand = ''
mkdir -p $out/bin $out/lib/lean mkdir -p $out/bin $out/lib/lean
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared_1}/* ${leanshared}/* ${Lake.sharedLib}/* $out/lib/lean/ ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${libInit_shared}/* ${leanshared}/* $out/lib/lean/
# put everything in a single final derivation so `IO.appDir` references work # put everything in a single final derivation so `IO.appDir` references work
cp ${lean}/bin/lean ${leanc}/bin/leanc ${Lake-Main.executable}/bin/lake $out/bin cp ${lean}/bin/lean ${leanc}/bin/leanc ${Lake-Main.executable}/bin/lake $out/bin
# NOTE: `lndir` will not override existing `bin/leanc` # NOTE: `lndir` will not override existing `bin/leanc`
@@ -153,13 +151,15 @@ lib.warn "The Nix-based build is deprecated" rec {
''; '';
meta.mainProgram = "lean"; meta.mainProgram = "lean";
}; };
cacheRoots = linkFarmFromDrvs "cacheRoots" ([ cacheRoots = linkFarmFromDrvs "cacheRoots" [
stage0 lean leanc lean-all iTree modDepsFiles depRoots Leanc.src stage0 lean leanc lean-all iTree modDepsFiles depRoots Leanc.src
] ++ map (lib: lib.oTree) stdlib); # .o files are not a runtime dependency on macOS because of lack of thin archives
Lean.oTree Lake.oTree
];
test = buildCMake { test = buildCMake {
name = "lean-test-${desc}"; name = "lean-test-${desc}";
realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ]; realSrc = lib.sourceByRegex src [ "src.*" "tests.*" ];
buildInputs = [ gmp libuv perl git cadical ]; buildInputs = [ gmp perl git ];
preConfigure = '' preConfigure = ''
cd src cd src
''; '';
@@ -170,7 +170,7 @@ lib.warn "The Nix-based build is deprecated" rec {
ln -sf ${lean-all}/* . ln -sf ${lean-all}/* .
''; '';
buildPhase = '' buildPhase = ''
ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)|leanlaketest_reverse-ffi|leanruntest_timeIO' -j$NIX_BUILD_CORES ctest --output-junit test-results.xml --output-on-failure -E 'leancomptest_(doc_example|foreign)' -j$NIX_BUILD_CORES
''; '';
installPhase = '' installPhase = ''
mkdir $out mkdir $out
@@ -178,7 +178,7 @@ lib.warn "The Nix-based build is deprecated" rec {
''; '';
}; };
update-stage0 = update-stage0 =
let cTree = symlinkJoin { name = "cs"; paths = map (lib: lib.cTree) (stdlib ++ [Lake-Main]); }; in let cTree = symlinkJoin { name = "cs"; paths = [ Init.cTree Lean.cTree ]; }; in
writeShellScriptBin "update-stage0" '' writeShellScriptBin "update-stage0" ''
CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/lib/update-stage0"} CSRCS=${cTree} CP_C_PARAMS="--dereference --no-preserve=all" ${src + "/script/lib/update-stage0"}
''; '';

View File

@@ -1,11 +1,11 @@
{ lean, lean-leanDeps ? lean, lean-final ? lean, leanc, { lean, lean-leanDeps ? lean, lean-final ? lean, leanc,
stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, substituteAll, symlinkJoin, linkFarmFromDrvs, stdenv, lib, coreutils, gnused, writeShellScriptBin, bash, lean-emacs, lean-vscode, nix, substituteAll, symlinkJoin, linkFarmFromDrvs,
runCommand, darwin, mkShell, ... }: runCommand, darwin, mkShell, ... }:
let lean-final' = lean-final; in let lean-final' = lean-final; in
lib.makeOverridable ( lib.makeOverridable (
{ name, src, fullSrc ? src, srcPrefix ? "", srcPath ? "$PWD/${srcPrefix}", { name, src, fullSrc ? src, srcPrefix ? "", srcPath ? "$PWD/${srcPrefix}",
# Lean dependencies. Each entry should be an output of buildLeanPackage. # Lean dependencies. Each entry should be an output of buildLeanPackage.
deps ? [ lean.Init lean.Std lean.Lean ], deps ? [ lean.Lean ],
# Static library dependencies. Each derivation `static` should contain a static library in the directory `${static}`. # Static library dependencies. Each derivation `static` should contain a static library in the directory `${static}`.
staticLibDeps ? [], staticLibDeps ? [],
# Whether to wrap static library inputs in a -Wl,--start-group [...] -Wl,--end-group to ensure dependencies are resolved. # Whether to wrap static library inputs in a -Wl,--start-group [...] -Wl,--end-group to ensure dependencies are resolved.
@@ -30,7 +30,7 @@ lib.makeOverridable (
pluginDeps ? [], pluginDeps ? [],
# `overrideAttrs` for `buildMod` # `overrideAttrs` for `buildMod`
overrideBuildModAttrs ? null, overrideBuildModAttrs ? null,
debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name, sharedLibName ? libName, debug ? false, leanFlags ? [], leancFlags ? [], linkFlags ? [], executableName ? lib.toLower name, libName ? name,
srcTarget ? "..#stage0", srcArgs ? "(\${args[*]})", lean-final ? lean-final' }@args: srcTarget ? "..#stage0", srcArgs ? "(\${args[*]})", lean-final ? lean-final' }@args:
with builtins; let with builtins; let
# "Init.Core" ~> "Init/Core" # "Init.Core" ~> "Init/Core"
@@ -197,6 +197,19 @@ with builtins; let
then map (m: m.module) header.imports then map (m: m.module) header.imports
else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" header.errors}"; else abort "errors while parsing imports of ${mod}:\n${lib.concatStringsSep "\n" header.errors}";
in mkMod mod (map (dep: if modDepsMap ? ${dep} then modCandidates.${dep} else externalModMap.${dep}) deps)) modDepsMap; in mkMod mod (map (dep: if modDepsMap ? ${dep} then modCandidates.${dep} else externalModMap.${dep}) deps)) modDepsMap;
makeEmacsWrapper = name: emacs: lean: writeShellScriptBin name ''
${emacs} --eval "(progn (setq lean4-rootdir \"${lean}\"))" "$@"
'';
makeVSCodeWrapper = name: lean: writeShellScriptBin name ''
PATH=${lean}/bin:$PATH ${lean-vscode}/bin/code "$@"
'';
printPaths = deps: writeShellScriptBin "print-paths" ''
echo '${toJSON {
oleanPath = [(depRoot "print-paths" deps)];
srcPath = ["."] ++ map (dep: dep.src) allExternalDeps;
loadDynlibPaths = map pathOfSharedLib (loadDynlibsOfDeps deps);
}}'
'';
expandGlob = g: expandGlob = g:
if typeOf g == "string" then [g] if typeOf g == "string" then [g]
else if g.glob == "one" then [g.mod] else if g.glob == "one" then [g.mod]
@@ -211,8 +224,7 @@ with builtins; let
allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs; allLinkFlags = lib.foldr (shared: acc: acc ++ [ "-L${shared}" "-l${shared.linkName or shared.name}" ]) linkFlags allNativeSharedLibs;
objects = mapAttrs (_: m: m.obj) mods'; objects = mapAttrs (_: m: m.obj) mods';
bintools = if stdenv.isDarwin then darwin.cctools else stdenv.cc.bintools.bintools; staticLib = runCommand "${name}-lib" { buildInputs = [ stdenv.cc.bintools.bintools ]; } ''
staticLib = runCommand "${name}-lib" { buildInputs = [ bintools ]; } ''
mkdir -p $out mkdir -p $out
ar Trcs $out/lib${libName}.a ${lib.concatStringsSep " " (map (drv: "${drv}/${drv.oPath}") (attrValues objects))}; ar Trcs $out/lib${libName}.a ${lib.concatStringsSep " " (map (drv: "${drv}/${drv.oPath}") (attrValues objects))};
''; '';
@@ -233,15 +245,59 @@ in rec {
cTree = symlinkJoin { name = "${name}-cTree"; paths = map (mod: mod.c) (attrValues mods); }; cTree = symlinkJoin { name = "${name}-cTree"; paths = map (mod: mod.c) (attrValues mods); };
oTree = symlinkJoin { name = "${name}-oTree"; paths = (attrValues objects); }; oTree = symlinkJoin { name = "${name}-oTree"; paths = (attrValues objects); };
iTree = symlinkJoin { name = "${name}-iTree"; paths = map (mod: mod.ilean) (attrValues mods); }; iTree = symlinkJoin { name = "${name}-iTree"; paths = map (mod: mod.ilean) (attrValues mods); };
sharedLib = mkSharedLib "lib${sharedLibName}" '' sharedLib = mkSharedLib "lib${libName}" ''
${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \ ${if stdenv.isDarwin then "-Wl,-force_load,${staticLib}/lib${libName}.a" else "-Wl,--whole-archive ${staticLib}/lib${libName}.a -Wl,--no-whole-archive"} \
${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}''; ${lib.concatStringsSep " " (map (d: "${d.sharedLib}/*") deps)}'';
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.leanshared}/*"; objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.libInit_shared}/* ${lean-final.leanshared}/*";
in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } '' in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } ''
mkdir -p $out/bin mkdir -p $out/bin
leanc ${staticLibLinkWrapper (lib.concatStringsSep " " (objPaths ++ map (d: "${d}/*.a") allStaticLibDeps))} \ leanc ${staticLibLinkWrapper (lib.concatStringsSep " " (objPaths ++ map (d: "${d}/*.a") allStaticLibDeps))} \
-o $out/bin/${executableName} \ -o $out/bin/${executableName} \
${lib.concatStringsSep " " allLinkFlags} ${lib.concatStringsSep " " allLinkFlags}
'') {}; '') {};
lean-package = writeShellScriptBin "lean" ''
LEAN_PATH=${modRoot}:$LEAN_PATH LEAN_SRC_PATH=$LEAN_SRC_PATH:${src} exec ${lean-final}/bin/lean "$@"
'';
emacs-package = makeEmacsWrapper "emacs-package" lean-package;
vscode-package = makeVSCodeWrapper "vscode-package" lean-package;
link-ilean = writeShellScriptBin "link-ilean" ''
dest=''${1:-.}
mkdir -p $dest/build/lib
ln -sf ${iTree}/* $dest/build/lib
'';
makePrintPathsFor = deps: mods: printPaths deps // mapAttrs (_: mod: makePrintPathsFor (deps ++ [mod]) mods) mods;
print-paths = makePrintPathsFor [] (mods' // externalModMap);
# `lean` wrapper that dynamically runs Nix for the actual `lean` executable so the same editor can be
# used for multiple projects/after upgrading the `lean` input/for editing both stage 1 and the tests
lean-bin-dev = substituteAll {
name = "lean";
dir = "bin";
src = ./lean-dev.in;
isExecutable = true;
srcRoot = fullSrc; # use root flake.nix in case of Lean repo
inherit bash nix srcTarget srcArgs;
};
lake-dev = substituteAll {
name = "lake";
dir = "bin";
src = ./lake-dev.in;
isExecutable = true;
srcRoot = fullSrc; # use root flake.nix in case of Lean repo
inherit bash nix srcTarget srcArgs;
};
lean-dev = symlinkJoin { name = "lean-dev"; paths = [ lean-bin-dev lake-dev ]; };
emacs-dev = makeEmacsWrapper "emacs-dev" "${lean-emacs}/bin/emacs" lean-dev;
emacs-path-dev = makeEmacsWrapper "emacs-path-dev" "emacs" lean-dev;
vscode-dev = makeVSCodeWrapper "vscode-dev" lean-dev;
devShell = mkShell {
buildInputs = [ nix ];
shellHook = ''
export LEAN_SRC_PATH="${srcPath}"
'';
};
}) })

View File

@@ -1,6 +1,9 @@
{ src, pkgs, ... } @ args: { src, pkgs, nix, ... } @ args:
with pkgs; with pkgs;
let let
nix-pinned = writeShellScriptBin "nix" ''
${nix.packages.${system}.default}/bin/nix --experimental-features 'nix-command flakes' --extra-substituters https://lean4.cachix.org/ --option warn-dirty false "$@"
'';
# https://github.com/NixOS/nixpkgs/issues/130963 # https://github.com/NixOS/nixpkgs/issues/130963
llvmPackages = if stdenv.isDarwin then llvmPackages_11 else llvmPackages_15; llvmPackages = if stdenv.isDarwin then llvmPackages_11 else llvmPackages_15;
cc = (ccacheWrapper.override rec { cc = (ccacheWrapper.override rec {
@@ -39,9 +42,40 @@ let
inherit (lean) stdenv; inherit (lean) stdenv;
lean = lean.stage1; lean = lean.stage1;
inherit (lean.stage1) leanc; inherit (lean.stage1) leanc;
inherit lean-emacs lean-vscode;
nix = nix-pinned;
})); }));
lean4-mode = emacsPackages.melpaBuild {
pname = "lean4-mode";
version = "1";
commit = "1";
src = args.lean4-mode;
packageRequires = with pkgs.emacsPackages.melpaPackages; [ dash f flycheck magit-section lsp-mode s ];
recipe = pkgs.writeText "recipe" ''
(lean4-mode
:repo "leanprover/lean4-mode"
:fetcher github
:files ("*.el" "data"))
'';
};
lean-emacs = emacsWithPackages [ lean4-mode ];
# updating might be nicer by building from source from a flake input, but this is good enough for now
vscode-lean4 = vscode-utils.extensionFromVscodeMarketplace {
name = "lean4";
publisher = "leanprover";
version = "0.0.63";
sha256 = "sha256-kjEex7L0F2P4pMdXi4NIZ1y59ywJVubqDqsoYagZNkI=";
};
lean-vscode = vscode-with-extensions.override {
vscodeExtensions = [ vscode-lean4 ];
};
in { in {
inherit cc buildLeanPackage llvmPackages; inherit cc lean4-mode buildLeanPackage llvmPackages vscode-lean4;
lean = lean.stage1;
stage0print-paths = lean.stage1.Lean.print-paths;
HEAD-as-stage0 = (lean.stage1.Lean.overrideArgs { srcTarget = "..#stage0-from-input.stage0"; srcArgs = "(--override-input lean-stage0 ..\?rev=$(git rev-parse HEAD) -- -Dinterpreter.prefer_native=false \"$@\")"; });
HEAD-as-stage1 = (lean.stage1.Lean.overrideArgs { srcTarget = "..\?rev=$(git rev-parse HEAD)#stage0"; });
nix = nix-pinned;
nixpkgs = pkgs; nixpkgs = pkgs;
ciShell = writeShellScriptBin "ciShell" '' ciShell = writeShellScriptBin "ciShell" ''
set -o pipefail set -o pipefail
@@ -49,4 +83,5 @@ in {
# prefix lines with cumulative and individual execution time # prefix lines with cumulative and individual execution time
"$@" |& ts -i "(%.S)]" | ts -s "[%M:%S" "$@" |& ts -i "(%.S)]" | ts -s "[%M:%S"
''; '';
} // lean.stage1 vscode = lean-vscode;
} // lean.stage1.Lean // lean.stage1 // lean

View File

@@ -1,22 +0,0 @@
Draft release notes
-------------------
This folder contains drafts of release notes for inclusion in `RELEASES.md`.
During the process to create a release candidate, we look through all the commits that make up the release
to prepare the release notes, and in that process we take these drafts into account.
Guidelines:
- You should prefer adding release notes to commit messages over adding anything to this folder.
A release note should briefly explain the impact of a change from a user's point of view.
Please mark these parts out with words such as **release notes** and/or **breaking changes**.
- It is not necessary to add anything to this folder. It is meant for larger features that span multiple PRs,
or for anything that would be helpful when preparing the release notes that might be missed
by someone reading through the change log.
- If the PR that adds a feature simultaneously adds a draft release note, including the PR number is not required
since it can be obtained from the git history for the file.
When release notes are prepared, all the draft release notes are deleted from this folder.
For release candidates beyond the first one, you can either update `RELEASE.md` directly
or continue to add drafts.
When a release is finalized, we will copy the completed release notes from `RELEASE.md` to the `master` branch.

View File

@@ -15,19 +15,4 @@ for f in $(git ls-files src ':!:src/lake/*' ':!:src/Leanc.lean'); do
cp $f stage0/$f cp $f stage0/$f
fi fi
done done
# special handling for Lake files due to its nested directory
# copy the README to ensure the `stage0/src/lake` directory is committed
for f in $(git ls-files 'src/lake/Lake/*' src/lake/Lake.lean src/lake/LakeMain.lean src/lake/README.md ':!:src/lakefile.toml'); do
if [[ $f == *.lean ]]; then
f=${f#src/lake}
f=${f%.lean}.c
mkdir -p $(dirname stage0/stdlib/$f)
cp ${CP_C_PARAMS:-} $CSRCS/$f stage0/stdlib/$f
else
mkdir -p $(dirname stage0/$f)
cp $f stage0/$f
fi
done
git add stage0 git add stage0

View File

@@ -1,12 +0,0 @@
#! /bin/env bash
# Open a Mathlib4 PR for benchmarking a given Lean 4 PR
set -euo pipefail
[ $# -eq 1 ] || (echo "usage: $0 <lean4 PR #>"; exit 1)
LEAN_PR=$1
PR_RESPONSE=$(gh api repos/leanprover-community/mathlib4/pulls -X POST -f head=lean-pr-testing-$LEAN_PR -f base=nightly-testing -f title="leanprover/lean4#$LEAN_PR benchmarking" -f draft=true -f body="ignore me")
PR_NUMBER=$(echo "$PR_RESPONSE" | jq '.number')
echo "opened https://github.com/leanprover-community/mathlib4/pull/$PR_NUMBER"
gh api repos/leanprover-community/mathlib4/issues/$PR_NUMBER/comments -X POST -f body="!bench" > /dev/null

View File

@@ -38,7 +38,7 @@ $CP $GLIBC/lib/*crt* llvm/lib/
$CP $GLIBC/lib/*crt* stage1/lib/ $CP $GLIBC/lib/*crt* stage1/lib/
# runtime # runtime
(cd llvm; $CP --parents lib/clang/*/lib/*/{clang_rt.*.o,libclang_rt.builtins*} ../stage1) (cd llvm; $CP --parents lib/clang/*/lib/*/{clang_rt.*.o,libclang_rt.builtins*} ../stage1)
$CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a $LIBUV/lib/libuv.a stage1/lib/ $CP llvm/lib/*/lib{c++,c++abi,unwind}.* $GMP/lib/libgmp.a stage1/lib/
# LLVM 15 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/' # LLVM 15 appears to ship the dependencies in 'llvm/lib/<target-triple>/' and 'llvm/include/<target-triple>/'
# but clang-15 that we use to compile is linked against 'llvm/lib/' and 'llvm/include' # but clang-15 that we use to compile is linked against 'llvm/lib/' and 'llvm/include'
# https://github.com/llvm/llvm-project/issues/54955 # https://github.com/llvm/llvm-project/issues/54955
@@ -48,8 +48,6 @@ $CP llvm-host/lib/*/lib{c++,c++abi,unwind}.* llvm-host/lib/
$CP -r llvm/include/*-*-* llvm-host/include/ $CP -r llvm/include/*-*-* llvm-host/include/
# glibc: use for linking (so Lean programs don't embed newer symbol versions), but not for running (because libc.so, librt.so, and ld.so must be compatible)! # glibc: use for linking (so Lean programs don't embed newer symbol versions), but not for running (because libc.so, librt.so, and ld.so must be compatible)!
$CP $GLIBC/lib/libc_nonshared.a stage1/lib/glibc $CP $GLIBC/lib/libc_nonshared.a stage1/lib/glibc
# libpthread_nonshared.a must be linked in order to be able to use `pthread_atfork(3)`. LibUV uses this function.
$CP $GLIBC/lib/libpthread_nonshared.a stage1/lib/glibc
for f in $GLIBC/lib/lib{c,dl,m,rt,pthread}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done for f in $GLIBC/lib/lib{c,dl,m,rt,pthread}-*; do b=$(basename $f); cp $f stage1/lib/glibc/${b%-*}.so; done
OPTIONS=() OPTIONS=()
echo -n " -DLEAN_STANDALONE=ON" echo -n " -DLEAN_STANDALONE=ON"
@@ -64,8 +62,8 @@ fi
# use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers), # use `-nostdinc` to make sure headers are not visible by default (in particular, not to `#include_next` in the clang headers),
# but do not change sysroot so users can still link against system libs # but do not change sysroot so users can still link against system libs
echo -n " -DLEANC_INTERNAL_FLAGS='-nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang" echo -n " -DLEANC_INTERNAL_FLAGS='-nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a ROOT/lib/glibc/libpthread_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -luv -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'" echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/glibc ROOT/lib/glibc/libc_nonshared.a -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual # when not using the above flags, link GMP dynamically/as usual
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -luv -lpthread -ldl -lrt -Wl,--no-as-needed'" echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -Wl,--no-as-needed'"
# do not set `LEAN_CC` for tests # do not set `LEAN_CC` for tests
echo -n " -DLEAN_TEST_VARS=''" echo -n " -DLEAN_TEST_VARS=''"

View File

@@ -9,7 +9,6 @@ set -uxo pipefail
# use full LLVM release for compiling C++ code, but subset for compiling C code and distribution # use full LLVM release for compiling C++ code, but subset for compiling C code and distribution
GMP=${GMP:-$(brew --prefix)} GMP=${GMP:-$(brew --prefix)}
LIBUV=${LIBUV:-$(brew --prefix)}
[[ -d llvm ]] || (mkdir llvm; gtar xf $1 --strip-components 1 --directory llvm) [[ -d llvm ]] || (mkdir llvm; gtar xf $1 --strip-components 1 --directory llvm)
[[ -d llvm-host ]] || if [[ "$#" -gt 1 ]]; then [[ -d llvm-host ]] || if [[ "$#" -gt 1 ]]; then
@@ -47,9 +46,8 @@ echo -n " -DLEAN_EXTRA_CXX_FLAGS='${EXTRA_FLAGS:-}'"
if [[ -L llvm-host ]]; then if [[ -L llvm-host ]]; then
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang" echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang"
gcp $GMP/lib/libgmp.a stage1/lib/ gcp $GMP/lib/libgmp.a stage1/lib/
gcp $LIBUV/lib/libuv.a stage1/lib/
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'" echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -luv'" echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp'"
else else
echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/15.0.1 ${EXTRA_FLAGS:-}'" echo -n " -DCMAKE_C_COMPILER=$PWD/llvm-host/bin/clang -DLEANC_OPTS='--sysroot $PWD/stage1 -resource-dir $PWD/stage1/lib/clang/15.0.1 ${EXTRA_FLAGS:-}'"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'" echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -L ROOT/lib/libc -fuse-ld=lld'"

View File

@@ -31,21 +31,15 @@ cp /clang64/lib/{crtbegin,crtend,crt2,dllcrt2}.o stage1/lib/
# runtime # runtime
(cd llvm; cp --parents lib/clang/*/lib/*/libclang_rt.builtins* ../stage1) (cd llvm; cp --parents lib/clang/*/lib/*/libclang_rt.builtins* ../stage1)
# further dependencies # further dependencies
# Note: even though we're linking against libraries like `libbcrypt.a` which appear to be static libraries from the file name, cp /clang64/lib/lib{m,bcrypt,mingw32,moldname,mingwex,msvcrt,pthread,advapi32,shell32,user32,kernel32,ucrtbase}.* /clang64/lib/libgmp.a llvm/lib/lib{c++,c++abi,unwind}.a stage1/lib/
# we're not actually linking statically against the code.
# Rather, `libbcrypt.a` is an import library (see https://en.wikipedia.org/wiki/Dynamic-link_library#Import_libraries) that just
# tells the compiler how to dynamically link against `bcrypt.dll` (which is located in the System32 folder).
# This distinction is relevant specifically for `libicu.a`/`icu.dll` because there we want updates to the time zone database to
# be delivered to users via Windows Update without having to recompile Lean or Lean programs.
cp /clang64/lib/lib{m,bcrypt,mingw32,moldname,mingwex,msvcrt,pthread,advapi32,shell32,user32,kernel32,ucrtbase,psapi,iphlpapi,userenv,ws2_32,dbghelp,ole32,icu}.* /clang64/lib/libgmp.a /clang64/lib/libuv.a llvm/lib/lib{c++,c++abi,unwind}.a stage1/lib/
echo -n " -DLEAN_STANDALONE=ON" echo -n " -DLEAN_STANDALONE=ON"
echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang.exe -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER=$PWD/llvm/bin/clang++.exe -DCMAKE_CXX_COMPILER_WORKS=1 -DLEAN_CXX_STDLIB='-lc++ -lc++abi'" echo -n " -DCMAKE_C_COMPILER=$PWD/stage1/bin/clang.exe -DCMAKE_C_COMPILER_WORKS=1 -DCMAKE_CXX_COMPILER=$PWD/llvm/bin/clang++.exe -DCMAKE_CXX_COMPILER_WORKS=1 -DLEAN_CXX_STDLIB='-lc++ -lc++abi'"
echo -n " -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_CXX_COMPILER=clang++" echo -n " -DSTAGE0_CMAKE_C_COMPILER=clang -DSTAGE0_CMAKE_CXX_COMPILER=clang++"
echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter /clang64/include/'" echo -n " -DLEAN_EXTRA_CXX_FLAGS='--sysroot $PWD/llvm -idirafter /clang64/include/'"
echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang.exe" echo -n " -DLEANC_INTERNAL_FLAGS='--sysroot ROOT -nostdinc -isystem ROOT/include/clang' -DLEANC_CC=ROOT/bin/clang.exe"
echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -static-libgcc -Wl,-Bstatic -lgmp $(pkg-config --static --libs libuv) -lunwind -Wl,-Bdynamic -fuse-ld=lld'" echo -n " -DLEANC_INTERNAL_LINKER_FLAGS='-L ROOT/lib -static-libgcc -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual. Always link ICU dynamically. # when not using the above flags, link GMP dynamically/as usual
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp $(pkg-config --libs libuv) -lucrtbase'" echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-lgmp -lucrtbase'"
# do not set `LEAN_CC` for tests # do not set `LEAN_CC` for tests
echo -n " -DAUTO_THREAD_FINALIZATION=OFF -DSTAGE0_AUTO_THREAD_FINALIZATION=OFF" echo -n " -DAUTO_THREAD_FINALIZATION=OFF -DSTAGE0_AUTO_THREAD_FINALIZATION=OFF"
echo -n " -DLEAN_TEST_VARS=''" echo -n " -DLEAN_TEST_VARS=''"

View File

@@ -1,6 +1,5 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
cmake_policy(SET CMP0054 NEW) cmake_policy(SET CMP0054 NEW)
cmake_policy(SET CMP0110 NEW)
if(NOT (${CMAKE_GENERATOR} MATCHES "Unix Makefiles")) if(NOT (${CMAKE_GENERATOR} MATCHES "Unix Makefiles"))
message(FATAL_ERROR "The only supported CMake generator at the moment is 'Unix Makefiles'") message(FATAL_ERROR "The only supported CMake generator at the moment is 'Unix Makefiles'")
endif() endif()
@@ -10,15 +9,13 @@ endif()
include(ExternalProject) include(ExternalProject)
project(LEAN CXX C) project(LEAN CXX C)
set(LEAN_VERSION_MAJOR 4) set(LEAN_VERSION_MAJOR 4)
set(LEAN_VERSION_MINOR 16) set(LEAN_VERSION_MINOR 9)
set(LEAN_VERSION_PATCH 0) set(LEAN_VERSION_PATCH 0)
set(LEAN_VERSION_IS_RELEASE 0) # This number is 1 in the release revision, and 0 otherwise. 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'") set(LEAN_SPECIAL_VERSION_DESC "" CACHE STRING "Additional version description like 'nightly-2018-03-11'")
set(LEAN_VERSION_STRING "${LEAN_VERSION_MAJOR}.${LEAN_VERSION_MINOR}.${LEAN_VERSION_PATCH}") set(LEAN_VERSION_STRING "${LEAN_VERSION_MAJOR}.${LEAN_VERSION_MINOR}.${LEAN_VERSION_PATCH}")
if (LEAN_SPECIAL_VERSION_DESC) if (LEAN_SPECIAL_VERSION_DESC)
string(APPEND LEAN_VERSION_STRING "-${LEAN_SPECIAL_VERSION_DESC}") string(APPEND LEAN_VERSION_STRING "-${LEAN_SPECIAL_VERSION_DESC}")
elseif (NOT LEAN_VERSION_IS_RELEASE)
string(APPEND LEAN_VERSION_STRING "-pre")
endif() endif()
set(LEAN_PLATFORM_TARGET "" CACHE STRING "LLVM triple of the target platform") set(LEAN_PLATFORM_TARGET "" CACHE STRING "LLVM triple of the target platform")
@@ -51,8 +48,6 @@ option(LLVM "LLVM" OFF)
option(USE_GITHASH "GIT_HASH" ON) option(USE_GITHASH "GIT_HASH" ON)
# When ON we install LICENSE files to CMAKE_INSTALL_PREFIX # When ON we install LICENSE files to CMAKE_INSTALL_PREFIX
option(INSTALL_LICENSE "INSTALL_LICENSE" ON) option(INSTALL_LICENSE "INSTALL_LICENSE" ON)
# When ON we install a copy of cadical
option(INSTALL_CADICAL "Install a copy of cadical" ON)
# When ON thread storage is automatically finalized, it assumes platform support pthreads. # When ON thread storage is automatically finalized, it assumes platform support pthreads.
# This option is important when using Lean as library that is invoked from a different programming language (e.g., Haskell). # This option is important when using Lean as library that is invoked from a different programming language (e.g., Haskell).
option(AUTO_THREAD_FINALIZATION "AUTO_THREAD_FINALIZATION" ON) option(AUTO_THREAD_FINALIZATION "AUTO_THREAD_FINALIZATION" ON)
@@ -78,7 +73,6 @@ option(USE_GMP "USE_GMP" ON)
# development-specific options # development-specific options
option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF) option(CHECK_OLEAN_VERSION "Only load .olean files compiled with the current version of Lean" OFF)
option(USE_LAKE "Use Lake instead of lean.mk for building core libs from language server" OFF)
set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make") set(LEAN_EXTRA_MAKE_OPTS "" CACHE STRING "extra options to lean --make")
set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`") set(LEANC_CC ${CMAKE_C_COMPILER} CACHE STRING "C compiler to use in `leanc`")
@@ -122,7 +116,7 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# From https://emscripten.org/docs/compiling/WebAssembly.html#backends: # From https://emscripten.org/docs/compiling/WebAssembly.html#backends:
# > The simple and safe thing is to pass all -s flags at both compile and link time. # > The simple and safe thing is to pass all -s flags at both compile and link time.
set(EMSCRIPTEN_SETTINGS "-s ALLOW_MEMORY_GROWTH=1 -fwasm-exceptions -pthread -flto") set(EMSCRIPTEN_SETTINGS "-s ALLOW_MEMORY_GROWTH=1 -fwasm-exceptions -pthread -flto")
string(APPEND LEANC_EXTRA_CC_FLAGS " -pthread") string(APPEND LEANC_EXTRA_FLAGS " -pthread")
string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_EMSCRIPTEN ${EMSCRIPTEN_SETTINGS}") string(APPEND LEAN_EXTRA_CXX_FLAGS " -D LEAN_EMSCRIPTEN ${EMSCRIPTEN_SETTINGS}")
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${EMSCRIPTEN_SETTINGS}") string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${EMSCRIPTEN_SETTINGS}")
endif() endif()
@@ -157,11 +151,7 @@ if ((${MULTI_THREAD} MATCHES "ON") AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin"))
endif () endif ()
# We want explicit stack probes in huge Lean stack frames for robust stack overflow detection # We want explicit stack probes in huge Lean stack frames for robust stack overflow detection
string(APPEND LEANC_EXTRA_CC_FLAGS " -fstack-clash-protection") string(APPEND LEANC_EXTRA_FLAGS " -fstack-clash-protection")
# This makes signed integer overflow guaranteed to match 2's complement.
string(APPEND CMAKE_CXX_FLAGS " -fwrapv")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fwrapv")
if(NOT MULTI_THREAD) if(NOT MULTI_THREAD)
message(STATUS "Disabled multi-thread support, it will not be safe to run multiple threads in parallel") message(STATUS "Disabled multi-thread support, it will not be safe to run multiple threads in parallel")
@@ -251,77 +241,6 @@ if("${USE_GMP}" MATCHES "ON")
endif() endif()
endif() endif()
# LibUV
if("${CMAKE_SYSTEM_NAME}" MATCHES "Emscripten")
# Only on WebAssembly we compile LibUV ourselves
set(LIBUV_EMSCRIPTEN_FLAGS "${EMSCRIPTEN_SETTINGS}")
# LibUV does not compile on WebAssembly without modifications because
# building LibUV on a platform requires including stub implementations
# for features not present on the target platform. This patch includes
# the minimum amount of stub implementations needed for successfully
# running Lean on WebAssembly and using LibUV's temporary file support.
# It still leaves several symbols completely undefined: uv__fs_event_close,
# uv__hrtime, uv__io_check_fd, uv__io_fork, uv__io_poll, uv__platform_invalidate_fd
# uv__platform_loop_delete, uv__platform_loop_init. Making additional
# LibUV features available on WebAssembly might require adapting the
# patch to include additional LibUV source files.
set(LIBUV_PATCH_IN "
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5e8e0166..f3b29134 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -317,6 +317,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL \"GNU\")
src/unix/hurd.c)
endif()
+if(CMAKE_SYSTEM_NAME STREQUAL \"Emscripten\")
+ list(APPEND uv_sources
+ src/unix/no-proctitle.c)
+endif()
+
if(CMAKE_SYSTEM_NAME STREQUAL \"Linux\")
list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112)
list(APPEND uv_libraries dl rt)
")
string(REPLACE "\n" "\\n" LIBUV_PATCH ${LIBUV_PATCH_IN})
ExternalProject_add(libuv
PREFIX libuv
GIT_REPOSITORY https://github.com/libuv/libuv
# Sync version with flake.nix
GIT_TAG v1.48.0
CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release -DLIBUV_BUILD_TESTS=OFF -DLIBUV_BUILD_SHARED=OFF -DCMAKE_AR=${CMAKE_AR} -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_C_FLAGS=${LIBUV_EMSCRIPTEN_FLAGS}
PATCH_COMMAND git reset --hard HEAD && printf "${LIBUV_PATCH}" > patch.diff && git apply patch.diff
BUILD_IN_SOURCE ON
INSTALL_COMMAND "")
set(LIBUV_INCLUDE_DIR "${CMAKE_BINARY_DIR}/libuv/src/libuv/include")
set(LIBUV_LIBRARIES "${CMAKE_BINARY_DIR}/libuv/src/libuv/libuv.a")
else()
find_package(LibUV 1.0.0 REQUIRED)
endif()
include_directories(${LIBUV_INCLUDE_DIR})
if(NOT LEAN_STANDALONE)
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${LIBUV_LIBRARIES}")
endif()
# Windows SDK (for ICU)
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# Pass 'tools' to skip MSVC version check (as MSVC/Visual Studio is not necessarily installed)
find_package(WindowsSDK REQUIRED COMPONENTS tools)
# This will give a semicolon-separated list of include directories
get_windowssdk_include_dirs(${WINDOWSSDK_LATEST_DIR} WINDOWSSDK_INCLUDE_DIRS)
# To successfully build against Windows SDK headers, the Windows SDK headers must have lower
# priority than other system headers, so use `-idirafter`. Unfortunately, CMake does not
# support this using `include_directories`.
string(REPLACE ";" "\" -idirafter \"" WINDOWSSDK_INCLUDE_DIRS "${WINDOWSSDK_INCLUDE_DIRS}")
string(APPEND CMAKE_CXX_FLAGS " -idirafter \"${WINDOWSSDK_INCLUDE_DIRS}\"")
string(APPEND LEAN_EXTRA_LINKER_FLAGS " -licu")
endif()
# ccache # ccache
if(CCACHE AND NOT CMAKE_CXX_COMPILER_LAUNCHER AND NOT CMAKE_C_COMPILER_LAUNCHER) if(CCACHE AND NOT CMAKE_CXX_COMPILER_LAUNCHER AND NOT CMAKE_C_COMPILER_LAUNCHER)
find_program(CCACHE_PATH ccache) find_program(CCACHE_PATH ccache)
@@ -380,11 +299,11 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH) cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH)
string(APPEND LEANSHARED_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}") string(APPEND LEANSHARED_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}")
endif() endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lleanrt") string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lStd -lLean -lnodefs.js -lleanrt") string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
else() else()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -lStd -Wl,--start-group -lInit -lleanrt -Wl,--end-group") string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
endif() endif()
set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags") set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags")
@@ -403,12 +322,7 @@ if(NOT LEAN_STANDALONE)
endif() endif()
# flags for user binaries = flags for toolchain binaries + Lake # flags for user binaries = flags for toolchain binaries + Lake
set(LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake") string(APPEND LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set(LEANC_SHARED_LINKER_FLAGS " ${TOOLCHAIN_SHARED_LINKER_FLAGS} -Wl,--as-needed -lLake_shared -Wl,--no-as-needed")
else()
set(LEANC_SHARED_LINKER_FLAGS " ${TOOLCHAIN_SHARED_LINKER_FLAGS} -lLake_shared")
endif()
if (LLVM) if (LLVM)
string(APPEND LEANSHARED_LINKER_FLAGS " -L${LLVM_CONFIG_LIBDIR} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS} ${LLVM_CONFIG_SYSTEM_LIBS}") string(APPEND LEANSHARED_LINKER_FLAGS " -L${LLVM_CONFIG_LIBDIR} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS} ${LLVM_CONFIG_SYSTEM_LIBS}")
@@ -451,22 +365,17 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic") string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
endif() endif()
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec") string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC") string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN") string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean") string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec") string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib") string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
string(APPEND LEANSHARED_1_LINKER_FLAGS " -install_name @rpath/libleanshared_1.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib") string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -install_name @rpath/libLake_shared.dylib")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean") string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND CMAKE_CXX_FLAGS " -fPIC") string(APPEND CMAKE_CXX_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fPIC") string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND LAKESHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libLake_shared.dll.a -Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLake.a.export -Wl,--no-whole-archive")
endif() endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -479,7 +388,7 @@ if(NOT(${CMAKE_SYSTEM_NAME} MATCHES "Windows") AND NOT(${CMAKE_SYSTEM_NAME} MATC
string(APPEND CMAKE_EXE_LINKER_FLAGS " -rdynamic") string(APPEND CMAKE_EXE_LINKER_FLAGS " -rdynamic")
# hide all other symbols # hide all other symbols
string(APPEND CMAKE_CXX_FLAGS " -fvisibility=hidden -fvisibility-inlines-hidden") string(APPEND CMAKE_CXX_FLAGS " -fvisibility=hidden -fvisibility-inlines-hidden")
string(APPEND LEANC_EXTRA_CC_FLAGS " -fvisibility=hidden") string(APPEND LEANC_EXTRA_FLAGS " -fvisibility=hidden")
endif() endif()
# On Windows, add bcrypt for random number generation # On Windows, add bcrypt for random number generation
@@ -491,8 +400,8 @@ endif()
# executable or `leanshared`, plugins would try to look them up at load time (even though they # executable or `leanshared`, plugins would try to look them up at load time (even though they
# are already loaded) and probably fail unless we set up LD_LIBRARY_PATH. # are already loaded) and probably fail unless we set up LD_LIBRARY_PATH.
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# import libraries created by the stdlib.make targets # import library created by the `leanshared` target
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared") string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared")
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup") string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
endif() endif()
@@ -505,7 +414,7 @@ endif()
# Git HASH # Git HASH
if(USE_GITHASH) if(USE_GITHASH)
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1 ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) get_git_head_revision(GIT_REFSPEC GIT_SHA1)
if(${GIT_SHA1} MATCHES "GITDIR-NOTFOUND") if(${GIT_SHA1} MATCHES "GITDIR-NOTFOUND")
message(STATUS "Failed to read git_sha1") message(STATUS "Failed to read git_sha1")
set(GIT_SHA1 "") set(GIT_SHA1 "")
@@ -544,26 +453,9 @@ include_directories(${CMAKE_BINARY_DIR}/include) # config.h etc., "public" head
string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE)
string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}") string(APPEND LEANC_OPTS " ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}}")
# Do embed flag for finding system headers and libraries in dev builds # Do embed flag for finding system libraries in dev builds
if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE) if(CMAKE_OSX_SYSROOT AND NOT LEAN_STANDALONE)
string(APPEND LEANC_EXTRA_CC_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}") string(APPEND LEANC_EXTRA_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
string(APPEND LEAN_EXTRA_LINKER_FLAGS " ${CMAKE_CXX_SYSROOT_FLAG}${CMAKE_OSX_SYSROOT}")
endif()
add_subdirectory(initialize)
add_subdirectory(shell)
# to be included in `leanshared` but not the smaller `leanshared_1` (as it would pull
# in the world)
add_library(leaninitialize STATIC $<TARGET_OBJECTS:initialize>)
set_target_properties(leaninitialize PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
OUTPUT_NAME leaninitialize)
add_library(leanshell STATIC util/shell.cpp)
set_target_properties(leanshell PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
OUTPUT_NAME leanshell)
if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--whole-archive -lleanmanifest -Wl,--no-whole-archive")
endif() endif()
if(${STAGE} GREATER 1) if(${STAGE} GREATER 1)
@@ -574,17 +466,13 @@ if(${STAGE} GREATER 1)
add_library(leanrt STATIC IMPORTED) add_library(leanrt STATIC IMPORTED)
set_target_properties(leanrt PROPERTIES set_target_properties(leanrt PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a") IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a")
add_library(leancpp_1 STATIC IMPORTED)
set_target_properties(leancpp_1 PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/temp/libleancpp_1.a")
add_library(leancpp STATIC IMPORTED) add_library(leancpp STATIC IMPORTED)
set_target_properties(leancpp PROPERTIES set_target_properties(leancpp PROPERTIES
IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a") IMPORTED_LOCATION "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
add_custom_target(copy-leancpp add_custom_target(copy-leancpp
COMMAND cmake -E copy_if_different "${PREV_STAGE}/runtime/libleanrt_initial-exec.a" "${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a" COMMAND cmake -E copy_if_different "${PREV_STAGE}/runtime/libleanrt_initial-exec.a" "${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a"
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleanrt.a" "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a" COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleanrt.a" "${CMAKE_BINARY_DIR}/lib/lean/libleanrt.a"
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a" COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/lean/libleancpp.a" "${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a")
COMMAND cmake -E copy_if_different "${PREV_STAGE}/lib/temp/libleancpp_1.a" "${CMAKE_BINARY_DIR}/lib/temp/libleancpp_1.a")
add_dependencies(leancpp copy-leancpp) add_dependencies(leancpp copy-leancpp)
if(LLVM) if(LLVM)
add_custom_target(copy-lean-h-bc add_custom_target(copy-lean-h-bc
@@ -593,10 +481,6 @@ if(${STAGE} GREATER 1)
endif() endif()
else() else()
add_subdirectory(runtime) add_subdirectory(runtime)
if("${CMAKE_SYSTEM_NAME}" MATCHES "Emscripten")
add_dependencies(leanrt libuv)
add_dependencies(leanrt_initial-exec libuv)
endif()
add_subdirectory(util) add_subdirectory(util)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:util>) set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:util>)
@@ -608,23 +492,14 @@ else()
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:constructions>) set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:constructions>)
add_subdirectory(library/compiler) add_subdirectory(library/compiler)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:compiler>) set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:compiler>)
add_subdirectory(initialize)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:initialize>)
# leancpp without `initialize` (see `leaninitialize` above) add_library(leancpp STATIC ${LEAN_OBJS})
add_library(leancpp_1 STATIC ${LEAN_OBJS})
set_target_properties(leancpp_1 PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/temp
OUTPUT_NAME leancpp_1)
add_library(leancpp STATIC ${LEAN_OBJS} $<TARGET_OBJECTS:initialize>)
set_target_properties(leancpp PROPERTIES set_target_properties(leancpp PROPERTIES
OUTPUT_NAME leancpp) OUTPUT_NAME leancpp)
endif() endif()
if((${STAGE} GREATER 0) AND CADICAL AND INSTALL_CADICAL)
add_custom_target(copy-cadical
COMMAND cmake -E copy_if_different "${CADICAL}" "${CMAKE_BINARY_DIR}/bin/cadical${CMAKE_EXECUTABLE_SUFFIX}")
add_dependencies(leancpp copy-cadical)
endif()
# MSYS2 bash usually handles Windows paths relatively well, but not when putting them in the PATH # MSYS2 bash usually handles Windows paths relatively well, but not when putting them in the PATH
string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin") string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
@@ -632,15 +507,25 @@ string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
# (also looks nicer in the build log) # (also looks nicer in the build log)
file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib) file(RELATIVE_PATH LIB ${LEAN_SOURCE_DIR} ${CMAKE_BINARY_DIR}/lib)
# set up libInit_shared only on Windows; see also stdlib.make.in
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(INIT_SHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libInit.a.export ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a -Wl,--no-whole-archive -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libInit_shared.dll.a")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LEANSHARED_LINKER_FLAGS "-Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libInit.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libLean.a -Wl,-force_load,${CMAKE_BINARY_DIR}/lib/lean/libleancpp.a ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive ${CMAKE_BINARY_DIR}/lib/temp/libLean.a.export -lleancpp -Wl,--no-whole-archive -lInit_shared -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
else()
set(LEANSHARED_LINKER_FLAGS "-Wl,--whole-archive -lInit -lLean -lleancpp -Wl,--no-whole-archive ${CMAKE_BINARY_DIR}/runtime/libleanrt_initial-exec.a ${LEANSHARED_LINKER_FLAGS}")
endif()
if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") if (${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# We do not use dynamic linking via leanshared for Emscripten to keep things # We do not use dynamic linking via leanshared for Emscripten to keep things
# simple. (And we are not interested in `Lake` anyway.) To use dynamic # simple. (And we are not interested in `Lake` anyway.) To use dynamic
# linking, we would probably have to set MAIN_MODULE=2 on `leanshared`, # linking, we would probably have to set MAIN_MODULE=2 on `leanshared`,
# SIDE_MODULE=2 on `lean`, and set CMAKE_SHARED_LIBRARY_SUFFIX to ".js". # SIDE_MODULE=2 on `lean`, and set CMAKE_SHARED_LIBRARY_SUFFIX to ".js".
# We set `ERROR_ON_UNDEFINED_SYMBOLS=0` because our build of LibUV does not string(APPEND LEAN_EXE_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
# define all symbols, see the comment about LibUV on WebAssembly further up
# in this file.
string(APPEND LEAN_EXE_LINKER_FLAGS " ${LIB}/temp/libleanshell.a ${TOOLCHAIN_STATIC_LINKER_FLAGS} ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1 -s ERROR_ON_UNDEFINED_SYMBOLS=0")
endif() endif()
# Build the compiler using the bootstrapped C sources for stage0, and use # Build the compiler using the bootstrapped C sources for stage0, and use
@@ -654,7 +539,7 @@ add_custom_target(make_stdlib ALL
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server # The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
# for a parallelized nested build, but CMake doesn't let us do that. # for a parallelized nested build, but CMake doesn't let us do that.
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage # We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Lean
VERBATIM) VERBATIM)
# if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode # if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode
@@ -674,13 +559,8 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
) )
add_custom_target(leanshared ALL add_custom_target(leanshared ALL
DEPENDS Init_shared leancpp DEPENDS Init_shared leancpp
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared_1${CMAKE_SHARED_LIBRARY_SUFFIX}
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX} COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX}
) )
add_custom_target(lake_shared ALL
DEPENDS leanshared
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libLake_shared${CMAKE_SHARED_LIBRARY_SUFFIX}
)
else() else()
add_custom_target(Init_shared ALL add_custom_target(Init_shared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR} WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
@@ -690,29 +570,23 @@ else()
add_custom_target(leanshared ALL add_custom_target(leanshared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR} WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS Init_shared leancpp_1 leancpp leanshell leaninitialize DEPENDS Init_shared leancpp
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
VERBATIM) VERBATIM)
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared_1 -lleanshared") string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared")
endif() endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") if(${STAGE} GREATER 0 AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
add_custom_target(lake_lib ALL if(NOT EXISTS ${LEAN_SOURCE_DIR}/lake/Lake.lean)
message(FATAL_ERROR "src/lake does not exist. Please check out the Lake submodule using `git submodule update --init src/lake`.")
endif()
add_custom_target(lake ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR} WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS leanshared DEPENDS leanshared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake
VERBATIM) VERBATIM)
add_custom_target(lake_shared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_lib
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make libLake_shared
VERBATIM)
add_custom_target(lake ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_shared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make lake
VERBATIM)
endif() endif()
if(PREV_STAGE) if(PREV_STAGE)
@@ -741,9 +615,7 @@ file(COPY ${LEAN_SOURCE_DIR}/bin/leanmake DESTINATION ${CMAKE_BINARY_DIR}/bin)
install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION bin) install(DIRECTORY "${CMAKE_BINARY_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION bin)
if (${STAGE} GREATER 0 AND CADICAL AND INSTALL_CADICAL) add_subdirectory(shell)
install(PROGRAMS "${CADICAL}" DESTINATION bin)
endif()
add_custom_target(clean-stdlib add_custom_target(clean-stdlib
COMMAND rm -rf "${CMAKE_BINARY_DIR}/lib" || true) COMMAND rm -rf "${CMAKE_BINARY_DIR}/lib" || true)
@@ -786,9 +658,3 @@ endif()
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}") string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}") string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE_MAKE "${CMAKE_EXE_LINKER_FLAGS_MAKE}")
configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make) configure_file(${LEAN_SOURCE_DIR}/stdlib.make.in ${CMAKE_BINARY_DIR}/stdlib.make)
if(USE_LAKE AND STAGE EQUAL 1)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../tests/lakefile.toml)
configure_file(${LEAN_SOURCE_DIR}/lakefile.toml.in ${LEAN_SOURCE_DIR}/../lakefile.toml)
endif()

View File

@@ -35,5 +35,3 @@ import Init.Ext
import Init.Omega import Init.Omega
import Init.MacroTrace import Init.MacroTrace
import Init.Grind import Init.Grind
import Init.While
import Init.Syntax

View File

@@ -37,26 +37,42 @@ theorem apply_ite (f : α → β) (P : Prop) [Decidable P] (x y : α) :
f (ite P x y) = ite P (f x) (f y) := f (ite P x y) = ite P (f x) (f y) :=
apply_dite f P (fun _ => x) (fun _ => y) apply_dite f P (fun _ => x) (fun _ => y)
@[simp] theorem dite_eq_left_iff {P : Prop} [Decidable P] {B : ¬ P α} :
dite P (fun _ => a) B = a h, B h = a := by
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
@[simp] theorem dite_eq_right_iff {P : Prop} [Decidable P] {A : P α} :
(dite P A fun _ => b) = b h, A h = b := by
by_cases P <;> simp [*, forall_prop_of_true, forall_prop_of_false]
@[simp] theorem ite_eq_left_iff {P : Prop} [Decidable P] : ite P a b = a ¬P b = a :=
dite_eq_left_iff
@[simp] theorem ite_eq_right_iff {P : Prop} [Decidable P] : ite P a b = b P a = b :=
dite_eq_right_iff
/-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/ /-- A `dite` whose results do not actually depend on the condition may be reduced to an `ite`. -/
@[simp] theorem dite_eq_ite [Decidable P] : (dite P (fun _ => a) fun _ => b) = ite P a b := rfl @[simp] theorem dite_eq_ite [Decidable P] : (dite P (fun _ => a) fun _ => b) = ite P a b := rfl
@[deprecated "Use `ite_eq_right_iff`" (since := "2024-09-18")] -- We don't mark this as `simp` as it is already handled by `ite_eq_right_iff`.
theorem ite_some_none_eq_none [Decidable P] : theorem ite_some_none_eq_none [Decidable P] :
(if P then some x else none) = none ¬ P := by (if P then some x else none) = none ¬ P := by
simp only [ite_eq_right_iff, reduceCtorEq] simp only [ite_eq_right_iff]
rfl rfl
@[deprecated "Use `Option.ite_none_right_eq_some`" (since := "2024-09-18")] @[simp] theorem ite_some_none_eq_some [Decidable P] :
theorem ite_some_none_eq_some [Decidable P] :
(if P then some x else none) = some y P x = y := by (if P then some x else none) = some y P x = y := by
split <;> simp_all split <;> simp_all
@[deprecated "Use `dite_eq_right_iff" (since := "2024-09-18")] -- This is not marked as `simp` as it is already handled by `dite_eq_right_iff`.
theorem dite_some_none_eq_none [Decidable P] {x : P α} : theorem dite_some_none_eq_none [Decidable P] {x : P α} :
(if h : P then some (x h) else none) = none ¬P := by (if h : P then some (x h) else none) = none ¬P := by
simp simp only [dite_eq_right_iff]
rfl
@[deprecated "Use `Option.dite_none_right_eq_some`" (since := "2024-09-18")] @[simp] theorem dite_some_none_eq_some [Decidable P] {x : P α} {y : α} :
theorem dite_some_none_eq_some [Decidable P] {x : P α} {y : α} :
(if h : P then some (x h) else none) = some y h : P, x h = y := by (if h : P then some (x h) else none) = some y h : P, x h = y := by
by_cases h : P <;> simp [h] by_cases h : P <;> simp only [h, dite_cond_eq_true, dite_cond_eq_false, Option.some.injEq,
false_iff, not_exists]
case pos => exact fun h_eq Exists.intro h h_eq, fun h_exists => h_exists.2
case neg => exact fun h_false _ h_false

View File

@@ -80,8 +80,6 @@ noncomputable scoped instance (priority := low) propDecidable (a : Prop) : Decid
noncomputable def decidableInhabited (a : Prop) : Inhabited (Decidable a) where noncomputable def decidableInhabited (a : Prop) : Inhabited (Decidable a) where
default := inferInstance default := inferInstance
instance (a : Prop) : Nonempty (Decidable a) := propDecidable a
noncomputable def typeDecidableEq (α : Sort u) : DecidableEq α := noncomputable def typeDecidableEq (α : Sort u) : DecidableEq α :=
fun _ _ => inferInstance fun _ _ => inferInstance
@@ -123,11 +121,11 @@ theorem propComplete (a : Prop) : a = True a = False :=
| Or.inl ha => Or.inl (eq_true ha) | Or.inl ha => Or.inl (eq_true ha)
| Or.inr hn => Or.inr (eq_false hn) | Or.inr hn => Or.inr (eq_false hn)
-- this supersedes byCases in Decidable -- this supercedes byCases in Decidable
theorem byCases {p q : Prop} (hpq : p q) (hnpq : ¬p q) : q := theorem byCases {p q : Prop} (hpq : p q) (hnpq : ¬p q) : q :=
Decidable.byCases (dec := propDecidable _) hpq hnpq Decidable.byCases (dec := propDecidable _) hpq hnpq
-- this supersedes byContradiction in Decidable -- this supercedes byContradiction in Decidable
theorem byContradiction {p : Prop} (h : ¬p False) : p := theorem byContradiction {p : Prop} (h : ¬p False) : p :=
Decidable.byContradiction (dec := propDecidable _) h Decidable.byContradiction (dec := propDecidable _) h
@@ -136,30 +134,6 @@ The left-to-right direction, double negation elimination (DNE),
is classically true but not constructively. -/ is classically true but not constructively. -/
@[simp] theorem not_not : ¬¬a a := Decidable.not_not @[simp] theorem not_not : ¬¬a a := Decidable.not_not
/-- Transfer decidability of `¬ p` to decidability of `p`. -/
-- This can not be an instance as it would be tried everywhere.
def decidable_of_decidable_not (p : Prop) [h : Decidable (¬ p)] : Decidable p :=
match h with
| isFalse h => isTrue (Classical.not_not.mp h)
| isTrue h => isFalse h
attribute [local instance] decidable_of_decidable_not in
/-- Negation of the condition `P : Prop` in a `dite` is the same as swapping the branches. -/
@[simp low] protected theorem dite_not [hn : Decidable (¬p)] (x : ¬p α) (y : ¬¬p α) :
dite (¬p) x y = dite p (fun h => y (not_not_intro h)) x := by
cases hn <;> rename_i g
· simp [not_not.mp g]
· simp [g]
attribute [local instance] decidable_of_decidable_not in
/-- Negation of the condition `P : Prop` in a `ite` is the same as swapping the branches. -/
@[simp low] protected theorem ite_not (p : Prop) [Decidable (¬ p)] (x y : α) : ite (¬p) x y = ite p y x :=
dite_not (fun _ => x) (fun _ => y)
attribute [local instance] decidable_of_decidable_not in
@[simp low] protected theorem decide_not (p : Prop) [Decidable (¬ p)] : decide (¬p) = !decide p :=
byCases (fun h : p => by simp_all) (fun h => by simp_all)
@[simp low] theorem not_forall {p : α Prop} : (¬ x, p x) x, ¬p x := Decidable.not_forall @[simp low] theorem not_forall {p : α Prop} : (¬ x, p x) x, ¬p x := Decidable.not_forall
theorem not_forall_not {p : α Prop} : (¬ x, ¬p x) x, p x := Decidable.not_forall_not theorem not_forall_not {p : α Prop} : (¬ x, ¬p x) x, p x := Decidable.not_forall_not
@@ -186,7 +160,7 @@ theorem not_iff : ¬(a ↔ b) ↔ (¬a ↔ b) := Decidable.not_iff
@[simp] theorem not_imp : ¬(a b) a ¬b := Decidable.not_imp_iff_and_not @[simp] theorem not_imp : ¬(a b) a ¬b := Decidable.not_imp_iff_and_not
@[simp] theorem imp_and_neg_imp_iff (p : Prop) {q : Prop} : (p q) (¬p q) q := @[simp] theorem imp_and_neg_imp_iff (p q : Prop) : (p q) (¬p q) q :=
Iff.intro (fun (a : _ _) => (Classical.em p).rec a.left a.right) Iff.intro (fun (a : _ _) => (Classical.em p).rec a.left a.right)
(fun a => And.intro (fun _ => a) (fun _ => a)) (fun a => And.intro (fun _ => a) (fun _ => a))

View File

@@ -8,42 +8,6 @@ import Init.Core
universe u v w universe u v w
/--
A `ForIn'` instance, which handles `for h : x in c do`,
can also handle `for x in x do` by ignoring `h`, and so provides a `ForIn` instance.
Note that this instance will cause a potentially non-defeq duplication if both `ForIn` and `ForIn'`
instances are provided for the same type.
-/
-- We set the priority to 500 so it is below the default,
-- but still above the low priority instance from `Stream`.
instance (priority := 500) instForInOfForIn' [ForIn' m ρ α d] : ForIn m ρ α where
forIn x b f := forIn' x b fun a _ => f a
@[simp] theorem forIn'_eq_forIn [d : Membership α ρ] [ForIn' m ρ α d] {β} [Monad m] (x : ρ) (b : β)
(f : (a : α) a x β m (ForInStep β)) (g : (a : α) β m (ForInStep β))
(h : a m b, f a m b = g a b) :
forIn' x b f = forIn x b g := by
simp [instForInOfForIn']
congr
apply funext
intro a
apply funext
intro m
apply funext
intro b
simp [h]
rfl
/-- Extract the value from a `ForInStep`, ignoring whether it is `done` or `yield`. -/
def ForInStep.value (x : ForInStep α) : α :=
match x with
| ForInStep.done b => b
| ForInStep.yield b => b
@[simp] theorem ForInStep.value_done (b : β) : (ForInStep.done b).value = b := rfl
@[simp] theorem ForInStep.value_yield (b : β) : (ForInStep.yield b).value = b := rfl
@[reducible] @[reducible]
def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β := def Functor.mapRev {f : Type u Type v} [Functor f] {α β : Type u} : f α (α β) f β :=
fun a f => f <$> a fun a f => f <$> a
@@ -64,7 +28,7 @@ Important instances include
* `Option`, where `failure := none` and `<|>` returns the left-most `some`. * `Option`, where `failure := none` and `<|>` returns the left-most `some`.
* Parser combinators typically provide an `Applicative` instance for error-handling and * Parser combinators typically provide an `Applicative` instance for error-handling and
backtracking. backtracking.
Error recovery and state can interact subtly. For example, the implementation of `Alternative` for `OptionT (StateT σ Id)` keeps modifications made to the state while recovering from failure, while `StateT σ (OptionT Id)` discards them. Error recovery and state can interact subtly. For example, the implementation of `Alternative` for `OptionT (StateT σ Id)` keeps modifications made to the state while recovering from failure, while `StateT σ (OptionT Id)` discards them.
-/ -/
-- NB: List instance is in mathlib. Once upstreamed, add -- NB: List instance is in mathlib. Once upstreamed, add

View File

@@ -131,7 +131,7 @@ protected def adapt {ε' α : Type u} (f : ε → ε') : ExceptT ε m α → Exc
end ExceptT end ExceptT
@[always_inline] @[always_inline]
instance (m : Type u Type v) (ε₁ : Type u) (ε₂ : Type u) [MonadExceptOf ε₁ m] : MonadExceptOf ε₁ (ExceptT ε₂ m) where instance (m : Type u Type v) (ε₁ : Type u) (ε₂ : Type u) [Monad m] [MonadExceptOf ε₁ m] : MonadExceptOf ε₁ (ExceptT ε₂ m) where
throw e := ExceptT.mk <| throwThe ε₁ e throw e := ExceptT.mk <| throwThe ε₁ e
tryCatch x handle := ExceptT.mk <| tryCatchThe ε₁ x handle tryCatch x handle := ExceptT.mk <| tryCatchThe ε₁ x handle

View File

@@ -34,7 +34,7 @@ instance : Monad (ExceptCpsT ε m) where
bind x f := fun _ k₁ k₂ => x _ (fun a => f a _ k₁ k₂) k₂ bind x f := fun _ k₁ k₂ => x _ (fun a => f a _ k₁ k₂) k₂
instance : LawfulMonad (ExceptCpsT σ m) := by instance : LawfulMonad (ExceptCpsT σ m) := by
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl refine' { .. } <;> intros <;> rfl
instance : MonadExceptOf ε (ExceptCpsT ε m) where instance : MonadExceptOf ε (ExceptCpsT ε m) where
throw e := fun _ _ k => k e throw e := fun _ _ k => k e

View File

@@ -9,7 +9,7 @@ import Init.Meta
open Function open Function
@[simp] theorem monadLift_self {m : Type u Type v} (x : m α) : monadLift x = x := @[simp] theorem monadLift_self [Monad m] (x : m α) : monadLift x = x :=
rfl rfl
/-- /--
@@ -33,10 +33,6 @@ attribute [simp] id_map
@[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x := @[simp] theorem id_map' [Functor m] [LawfulFunctor m] (x : m α) : (fun a => a) <$> x = x :=
id_map x id_map x
@[simp] theorem Functor.map_map [Functor f] [LawfulFunctor f] (m : α β) (g : β γ) (x : f α) :
g <$> m <$> x = (fun a => g (m a)) <$> x :=
(comp_map _ _ _).symm
/-- /--
The `Applicative` typeclass only contains the operations of an applicative functor. The `Applicative` typeclass only contains the operations of an applicative functor.
`LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor: `LawfulApplicative` further asserts that these operations satisfy the laws of an applicative functor:
@@ -87,16 +83,12 @@ class LawfulMonad (m : Type u → Type v) [Monad m] extends LawfulApplicative m
seq_assoc x g h := (by simp [ bind_pure_comp, bind_map, bind_assoc, pure_bind]) seq_assoc x g h := (by simp [ bind_pure_comp, bind_map, bind_assoc, pure_bind])
export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc) export LawfulMonad (bind_pure_comp bind_map pure_bind bind_assoc)
attribute [simp] pure_bind bind_assoc bind_pure_comp attribute [simp] pure_bind bind_assoc
@[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by @[simp] theorem bind_pure [Monad m] [LawfulMonad m] (x : m α) : x >>= pure = x := by
show x >>= (fun a => pure (id a)) = x show x >>= (fun a => pure (id a)) = x
rw [bind_pure_comp, id_map] rw [bind_pure_comp, id_map]
/--
Use `simp [← bind_pure_comp]` rather than `simp [map_eq_pure_bind]`,
as `bind_pure_comp` is in the default simp set, so also using `map_eq_pure_bind` would cause a loop.
-/
theorem map_eq_pure_bind [Monad m] [LawfulMonad m] (f : α β) (x : m α) : f <$> x = x >>= fun a => pure (f a) := by theorem map_eq_pure_bind [Monad m] [LawfulMonad m] (f : α β) (x : m α) : f <$> x = x >>= fun a => pure (f a) := by
rw [ bind_pure_comp] rw [ bind_pure_comp]
@@ -117,24 +109,10 @@ theorem seq_eq_bind {α β : Type u} [Monad m] [LawfulMonad m] (mf : m (α
theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *> y = x >>= fun _ => y := by theorem seqRight_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x *> y = x >>= fun _ => y := by
rw [seqRight_eq] rw [seqRight_eq]
simp only [map_eq_pure_bind, const, seq_eq_bind_map, bind_assoc, pure_bind, id_eq, bind_pure] simp [map_eq_pure_bind, seq_eq_bind_map, const]
theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y = x >>= fun a => y >>= fun _ => pure a := by theorem seqLeft_eq_bind [Monad m] [LawfulMonad m] (x : m α) (y : m β) : x <* y = x >>= fun a => y >>= fun _ => pure a := by
rw [seqLeft_eq] rw [seqLeft_eq]; simp [map_eq_pure_bind, seq_eq_bind_map]
simp only [map_eq_pure_bind, seq_eq_bind_map, bind_assoc, pure_bind, const_apply]
@[simp] theorem map_bind [Monad m] [LawfulMonad m] (f : β γ) (x : m α) (g : α m β) :
f <$> (x >>= g) = x >>= fun a => f <$> g a := by
rw [ bind_pure_comp, LawfulMonad.bind_assoc]
simp [bind_pure_comp]
@[simp] theorem bind_map_left [Monad m] [LawfulMonad m] (f : α β) (x : m α) (g : β m γ) :
((f <$> x) >>= fun b => g b) = (x >>= fun a => g (f a)) := by
rw [ bind_pure_comp]
simp only [bind_assoc, pure_bind]
@[simp] theorem Functor.map_unit [Monad m] [LawfulMonad m] {a : m PUnit} : (fun _ => PUnit.unit) <$> a = a := by
simp [map]
/-- /--
An alternative constructor for `LawfulMonad` which has more An alternative constructor for `LawfulMonad` which has more
@@ -175,7 +153,7 @@ namespace Id
@[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl @[simp] theorem pure_eq (a : α) : (pure a : Id α) = a := rfl
instance : LawfulMonad Id := by instance : LawfulMonad Id := by
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl refine' { .. } <;> intros <;> rfl
end Id end Id
@@ -183,9 +161,9 @@ end Id
instance : LawfulMonad Option := LawfulMonad.mk' instance : LawfulMonad Option := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl) (id_map := fun x => by cases x <;> rfl)
(pure_bind := fun _ _ => rfl) (pure_bind := fun x f => rfl)
(bind_assoc := fun x _ _ => by cases x <;> rfl) (bind_assoc := fun x f g => by cases x <;> rfl)
(bind_pure_comp := fun _ x => by cases x <;> rfl) (bind_pure_comp := fun f x => by cases x <;> rfl)
instance : LawfulApplicative Option := inferInstance instance : LawfulApplicative Option := inferInstance
instance : LawfulFunctor Option := inferInstance instance : LawfulFunctor Option := inferInstance

View File

@@ -7,7 +7,6 @@ prelude
import Init.Control.Lawful.Basic import Init.Control.Lawful.Basic
import Init.Control.Except import Init.Control.Except
import Init.Control.StateRef import Init.Control.StateRef
import Init.Ext
open Function open Function
@@ -15,7 +14,7 @@ open Function
namespace ExceptT namespace ExceptT
@[ext] theorem ext {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by theorem ext [Monad m] {x y : ExceptT ε m α} (h : x.run = y.run) : x = y := by
simp [run] at h simp [run] at h
assumption assumption
@@ -26,7 +25,7 @@ namespace ExceptT
@[simp] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl @[simp] theorem run_throw [Monad m] : run (throw e : ExceptT ε m β) = pure (Except.error e) := rfl
@[simp] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by @[simp] theorem run_bind_lift [Monad m] [LawfulMonad m] (x : m α) (f : α ExceptT ε m β) : run (ExceptT.lift x >>= f : ExceptT ε m β) = x >>= fun a => run (f a) := by
simp [ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont] simp[ExceptT.run, ExceptT.lift, bind, ExceptT.bind, ExceptT.mk, ExceptT.bindCont, map_eq_pure_bind]
@[simp] theorem bind_throw [Monad m] [LawfulMonad m] (f : α ExceptT ε m β) : (throw e >>= f) = throw e := by @[simp] theorem bind_throw [Monad m] [LawfulMonad m] (f : α ExceptT ε m β) : (throw e >>= f) = throw e := by
simp [throw, throwThe, MonadExceptOf.throw, bind, ExceptT.bind, ExceptT.bindCont, ExceptT.mk] simp [throw, throwThe, MonadExceptOf.throw, bind, ExceptT.bind, ExceptT.bindCont, ExceptT.mk]
@@ -44,14 +43,14 @@ theorem run_bind [Monad m] (x : ExceptT ε m α)
@[simp] theorem run_map [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α) @[simp] theorem run_map [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α)
: (f <$> x).run = Except.map f <$> x.run := by : (f <$> x).run = Except.map f <$> x.run := by
simp [Functor.map, ExceptT.map, bind_pure_comp] simp [Functor.map, ExceptT.map, map_eq_pure_bind]
apply bind_congr apply bind_congr
intro a; cases a <;> simp [Except.map] intro a; cases a <;> simp [Except.map]
protected theorem seq_eq {α β ε : Type u} [Monad m] (mf : ExceptT ε m (α β)) (x : ExceptT ε m α) : mf <*> x = mf >>= fun f => f <$> x := protected theorem seq_eq {α β ε : Type u} [Monad m] (mf : ExceptT ε m (α β)) (x : ExceptT ε m α) : mf <*> x = mf >>= fun f => f <$> x :=
rfl rfl
protected theorem bind_pure_comp [Monad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by protected theorem bind_pure_comp [Monad m] [LawfulMonad m] (f : α β) (x : ExceptT ε m α) : x >>= pure f = f <$> x := by
intros; rfl intros; rfl
protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by protected theorem seqLeft_eq {α β ε : Type u} {m : Type u Type v} [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x <* y = const β <$> x <*> y := by
@@ -63,7 +62,7 @@ protected theorem seqLeft_eq {α β ε : Type u} {m : Type u → Type v} [Monad
intro intro
| Except.error _ => simp | Except.error _ => simp
| Except.ok _ => | Except.ok _ =>
simp [bind_pure_comp]; apply bind_congr; intro b; simp [map_eq_pure_bind]; apply bind_congr; intro b;
cases b <;> simp [comp, Except.map, const] cases b <;> simp [comp, Except.map, const]
protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by protected theorem seqRight_eq [Monad m] [LawfulMonad m] (x : ExceptT ε m α) (y : ExceptT ε m β) : x *> y = const α id <$> x <*> y := by
@@ -85,19 +84,14 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (ExceptT ε m) where
pure_bind := by intros; apply ext; simp [run_bind] pure_bind := by intros; apply ext; simp [run_bind]
bind_assoc := by intros; apply ext; simp [run_bind]; apply bind_congr; intro a; cases a <;> simp bind_assoc := by intros; apply ext; simp [run_bind]; apply bind_congr; intro a; cases a <;> simp
@[simp] theorem map_throw [Monad m] [LawfulMonad m] {α β : Type _} (f : α β) (e : ε) :
f <$> (throw e : ExceptT ε m α) = (throw e : ExceptT ε m β) := by
simp only [ExceptT.instMonad, ExceptT.map, ExceptT.mk, throw, throwThe, MonadExceptOf.throw,
pure_bind]
end ExceptT end ExceptT
/-! # Except -/ /-! # Except -/
instance : LawfulMonad (Except ε) := LawfulMonad.mk' instance : LawfulMonad (Except ε) := LawfulMonad.mk'
(id_map := fun x => by cases x <;> rfl) (id_map := fun x => by cases x <;> rfl)
(pure_bind := fun _ _ => rfl) (pure_bind := fun a f => rfl)
(bind_assoc := fun a _ _ => by cases a <;> rfl) (bind_assoc := fun a f g => by cases a <;> rfl)
instance : LawfulApplicative (Except ε) := inferInstance instance : LawfulApplicative (Except ε) := inferInstance
instance : LawfulFunctor (Except ε) := inferInstance instance : LawfulFunctor (Except ε) := inferInstance
@@ -106,7 +100,7 @@ instance : LawfulFunctor (Except ε) := inferInstance
namespace ReaderT namespace ReaderT
@[ext] theorem ext {x y : ReaderT ρ m α} (h : ctx, x.run ctx = y.run ctx) : x = y := by theorem ext {x y : ReaderT ρ m α} (h : ctx, x.run ctx = y.run ctx) : x = y := by
simp [run] at h simp [run] at h
exact funext h exact funext h
@@ -168,7 +162,7 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (StateRefT' ω σ m) :=
namespace StateT namespace StateT
@[ext] theorem ext {x y : StateT σ m α} (h : s, x.run s = y.run s) : x = y := theorem ext {x y : StateT σ m α} (h : s, x.run s = y.run s) : x = y :=
funext h funext h
@[simp] theorem run'_eq [Monad m] (x : StateT σ m α) (s : σ) : run' x s = (·.1) <$> run x s := @[simp] theorem run'_eq [Monad m] (x : StateT σ m α) (s : σ) : run' x s = (·.1) <$> run x s :=
@@ -181,7 +175,7 @@ namespace StateT
simp [bind, StateT.bind, run] simp [bind, StateT.bind, run]
@[simp] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by @[simp] theorem run_map {α β σ : Type u} [Monad m] [LawfulMonad m] (f : α β) (x : StateT σ m α) (s : σ) : (f <$> x).run s = (fun (p : α × σ) => (f p.1, p.2)) <$> x.run s := by
simp [Functor.map, StateT.map, run, bind_pure_comp] simp [Functor.map, StateT.map, run, map_eq_pure_bind]
@[simp] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl @[simp] theorem run_get [Monad m] (s : σ) : (get : StateT σ m σ).run s = pure (s, s) := rfl
@@ -194,35 +188,35 @@ namespace StateT
@[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl @[simp] theorem run_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) : (StateT.lift x : StateT σ m α).run s = x >>= fun a => pure (a, s) := rfl
theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by @[simp] theorem run_bind_lift {α σ : Type u} [Monad m] [LawfulMonad m] (x : m α) (f : α StateT σ m β) (s : σ) : (StateT.lift x >>= f).run s = x >>= fun a => (f a).run s := by
simp [StateT.lift, StateT.run, bind, StateT.bind] simp [StateT.lift, StateT.run, bind, StateT.bind]
@[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl @[simp] theorem run_monadLift {α σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) : (monadLift x : StateT σ m α).run s = (monadLift x : m α) >>= fun a => pure (a, s) := rfl
@[simp] theorem run_monadMap [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ) : @[simp] theorem run_monadMap [Monad m] [MonadFunctor n m] (f : {β : Type u} n β n β) (x : StateT σ m α) (s : σ)
(monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl : (monadMap @f x : StateT σ m α).run s = monadMap @f (x.run s) := rfl
@[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by @[simp] theorem run_seq {α β σ : Type u} [Monad m] [LawfulMonad m] (f : StateT σ m (α β)) (x : StateT σ m α) (s : σ) : (f <*> x).run s = (f.run s >>= fun fs => (fun (p : α × σ) => (fs.1 p.1, p.2)) <$> x.run fs.2) := by
show (f >>= fun g => g <$> x).run s = _ show (f >>= fun g => g <$> x).run s = _
simp simp
@[simp] theorem run_seqRight [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by @[simp] theorem run_seqRight [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x *> y).run s = (x.run s >>= fun p => y.run p.2) := by
show (x >>= fun _ => y).run s = _ show (x >>= fun _ => y).run s = _
simp simp
@[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by @[simp] theorem run_seqLeft {α β σ : Type u} [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) (s : σ) : (x <* y).run s = (x.run s >>= fun p => y.run p.2 >>= fun p' => pure (p.1, p'.2)) := by
show (x >>= fun a => y >>= fun _ => pure a).run s = _ show (x >>= fun a => y >>= fun _ => pure a).run s = _
simp simp
theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by theorem seqRight_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x *> y = const α id <$> x <*> y := by
apply ext; intro s apply ext; intro s
simp [bind_pure_comp, const] simp [map_eq_pure_bind, const]
apply bind_congr; intro p; cases p apply bind_congr; intro p; cases p
simp [Prod.eta] simp [Prod.eta]
theorem seqLeft_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x <* y = const β <$> x <*> y := by theorem seqLeft_eq [Monad m] [LawfulMonad m] (x : StateT σ m α) (y : StateT σ m β) : x <* y = const β <$> x <*> y := by
apply ext; intro s apply ext; intro s
simp [bind_pure_comp] simp [map_eq_pure_bind]
instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
id_map := by intros; apply ext; intros; simp[Prod.eta] id_map := by intros; apply ext; intros; simp[Prod.eta]
@@ -230,7 +224,7 @@ instance [Monad m] [LawfulMonad m] : LawfulMonad (StateT σ m) where
seqLeft_eq := seqLeft_eq seqLeft_eq := seqLeft_eq
seqRight_eq := seqRight_eq seqRight_eq := seqRight_eq
pure_seq := by intros; apply ext; intros; simp pure_seq := by intros; apply ext; intros; simp
bind_pure_comp := by intros; apply ext; intros; simp bind_pure_comp := by intros; apply ext; intros; simp; apply LawfulMonad.bind_pure_comp
bind_map := by intros; rfl bind_map := by intros; rfl
pure_bind := by intros; apply ext; intros; simp pure_bind := by intros; apply ext; intros; simp
bind_assoc := by intros; apply ext; intros; simp bind_assoc := by intros; apply ext; intros; simp

View File

@@ -67,7 +67,7 @@ instance : MonadExceptOf Unit (OptionT m) where
throw := fun _ => OptionT.fail throw := fun _ => OptionT.fail
tryCatch := OptionT.tryCatch tryCatch := OptionT.tryCatch
instance (ε : Type u) [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where instance (ε : Type u) [Monad m] [MonadExceptOf ε m] : MonadExceptOf ε (OptionT m) where
throw e := OptionT.mk <| throwThe ε e throw e := OptionT.mk <| throwThe ε e
tryCatch x handle := OptionT.mk <| tryCatchThe ε x handle tryCatch x handle := OptionT.mk <| tryCatchThe ε x handle

View File

@@ -32,7 +32,7 @@ instance : MonadControl m (ReaderT ρ m) where
restoreM x _ := x restoreM x _ := x
@[always_inline] @[always_inline]
instance ReaderT.tryFinally [MonadFinally m] : MonadFinally (ReaderT ρ m) where instance ReaderT.tryFinally [MonadFinally m] [Monad m] : MonadFinally (ReaderT ρ m) where
tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx) tryFinally' x h ctx := tryFinally' (x ctx) (fun a? => h a? ctx)
@[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id @[reducible] def ReaderM (ρ : Type u) := ReaderT ρ Id

View File

@@ -87,7 +87,7 @@ protected def lift {α : Type u} (t : m α) : StateT σ m α :=
instance : MonadLift m (StateT σ m) := StateT.lift instance : MonadLift m (StateT σ m) := StateT.lift
@[always_inline] @[always_inline]
instance (σ m) : MonadFunctor m (StateT σ m) := fun f x s => f (x s) instance (σ m) [Monad m] : MonadFunctor m (StateT σ m) := fun f x s => f (x s)
@[always_inline] @[always_inline]
instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := { instance (ε) [MonadExceptOf ε m] : MonadExceptOf ε (StateT σ m) := {

View File

@@ -14,18 +14,16 @@ def StateCpsT (σ : Type u) (m : Type u → Type v) (α : Type u) := (δ : Type
namespace StateCpsT namespace StateCpsT
variable {α σ : Type u} {m : Type u Type v}
@[always_inline, inline] @[always_inline, inline]
def runK (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β := def runK {α σ : Type u} {m : Type u Type v} (x : StateCpsT σ m α) (s : σ) (k : α σ m β) : m β :=
x _ s k x _ s k
@[always_inline, inline] @[always_inline, inline]
def run [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) := def run {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m (α × σ) :=
runK x s (fun a s => pure (a, s)) runK x s (fun a s => pure (a, s))
@[always_inline, inline] @[always_inline, inline]
def run' [Monad m] (x : StateCpsT σ m α) (s : σ) : m α := def run' {α σ : Type u} {m : Type u Type v} [Monad m] (x : StateCpsT σ m α) (s : σ) : m α :=
runK x s (fun a _ => pure a) runK x s (fun a _ => pure a)
@[always_inline] @[always_inline]
@@ -35,7 +33,7 @@ instance : Monad (StateCpsT σ m) where
bind x f := fun δ s k => x δ s fun a s => f a δ s k bind x f := fun δ s k => x δ s fun a s => f a δ s k
instance : LawfulMonad (StateCpsT σ m) := by instance : LawfulMonad (StateCpsT σ m) := by
refine LawfulMonad.mk' _ ?_ ?_ ?_ <;> intros <;> rfl refine' { .. } <;> intros <;> rfl
@[always_inline] @[always_inline]
instance : MonadStateOf σ (StateCpsT σ m) where instance : MonadStateOf σ (StateCpsT σ m) where
@@ -50,29 +48,29 @@ protected def lift [Monad m] (x : m α) : StateCpsT σ m α :=
instance [Monad m] : MonadLift m (StateCpsT σ m) where instance [Monad m] : MonadLift m (StateCpsT σ m) where
monadLift := StateCpsT.lift monadLift := StateCpsT.lift
@[simp] theorem runK_pure (a : α) (s : σ) (k : α σ m β) : (pure a : StateCpsT σ m α).runK s k = k a s := rfl @[simp] theorem runK_pure {m : Type u Type v} (a : α) (s : σ) (k : α σ m β) : (pure a : StateCpsT σ m α).runK s k = k a s := rfl
@[simp] theorem runK_get (s : σ) (k : σ σ m β) : (get : StateCpsT σ m σ).runK s k = k s s := rfl @[simp] theorem runK_get {m : Type u Type v} (s : σ) (k : σ σ m β) : (get : StateCpsT σ m σ).runK s k = k s s := rfl
@[simp] theorem runK_set (s s' : σ) (k : PUnit σ m β) : (set s' : StateCpsT σ m PUnit).runK s k = k s' := rfl @[simp] theorem runK_set {m : Type u Type v} (s s' : σ) (k : PUnit σ m β) : (set s' : StateCpsT σ m PUnit).runK s k = k s' := rfl
@[simp] theorem runK_modify (f : σ σ) (s : σ) (k : PUnit σ m β) : (modify f : StateCpsT σ m PUnit).runK s k = k (f s) := rfl @[simp] theorem runK_modify {m : Type u Type v} (f : σ σ) (s : σ) (k : PUnit σ m β) : (modify f : StateCpsT σ m PUnit).runK s k = k (f s) := rfl
@[simp] theorem runK_lift [Monad m] (x : m α) (s : σ) (k : α σ m β) : (StateCpsT.lift x : StateCpsT σ m α).runK s k = x >>= (k . s) := rfl @[simp] theorem runK_lift {α σ : Type u} [Monad m] (x : m α) (s : σ) (k : α σ m β) : (StateCpsT.lift x : StateCpsT σ m α).runK s k = x >>= (k . s) := rfl
@[simp] theorem runK_monadLift [Monad m] [MonadLiftT n m] (x : n α) (s : σ) (k : α σ m β) @[simp] theorem runK_monadLift {σ : Type u} [Monad m] [MonadLiftT n m] (x : n α) (s : σ) (k : α σ m β)
: (monadLift x : StateCpsT σ m α).runK s k = (monadLift x : m α) >>= (k . s) := rfl : (monadLift x : StateCpsT σ m α).runK s k = (monadLift x : m α) >>= (k . s) := rfl
@[simp] theorem runK_bind_pure (a : α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ) : (pure a >>= f).runK s k = (f a).runK s k := rfl @[simp] theorem runK_bind_pure {α σ : Type u} [Monad m] (a : α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ) : (pure a >>= f).runK s k = (f a).runK s k := rfl
@[simp] theorem runK_bind_lift [Monad m] (x : m α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ) @[simp] theorem runK_bind_lift {α σ : Type u} [Monad m] (x : m α) (f : α StateCpsT σ m β) (s : σ) (k : β σ m γ)
: (StateCpsT.lift x >>= f).runK s k = x >>= fun a => (f a).runK s k := rfl : (StateCpsT.lift x >>= f).runK s k = x >>= fun a => (f a).runK s k := rfl
@[simp] theorem runK_bind_get (f : σ StateCpsT σ m β) (s : σ) (k : β σ m γ) : (get >>= f).runK s k = (f s).runK s k := rfl @[simp] theorem runK_bind_get {σ : Type u} [Monad m] (f : σ StateCpsT σ m β) (s : σ) (k : β σ m γ) : (get >>= f).runK s k = (f s).runK s k := rfl
@[simp] theorem runK_bind_set (f : PUnit StateCpsT σ m β) (s s' : σ) (k : β σ m γ) : (set s' >>= f).runK s k = (f ).runK s' k := rfl @[simp] theorem runK_bind_set {σ : Type u} [Monad m] (f : PUnit StateCpsT σ m β) (s s' : σ) (k : β σ m γ) : (set s' >>= f).runK s k = (f ).runK s' k := rfl
@[simp] theorem runK_bind_modify (f : σ σ) (g : PUnit StateCpsT σ m β) (s : σ) (k : β σ m γ) : (modify f >>= g).runK s k = (g ).runK (f s) k := rfl @[simp] theorem runK_bind_modify {σ : Type u} [Monad m] (f : σ σ) (g : PUnit StateCpsT σ m β) (s : σ) (k : β σ m γ) : (modify f >>= g).runK s k = (g ).runK (f s) k := rfl
@[simp] theorem run_eq [Monad m] (x : StateCpsT σ m α) (s : σ) : x.run s = x.runK s (fun a s => pure (a, s)) := rfl @[simp] theorem run_eq [Monad m] (x : StateCpsT σ m α) (s : σ) : x.run s = x.runK s (fun a s => pure (a, s)) := rfl

View File

@@ -6,7 +6,8 @@ Authors: Leonardo de Moura, Sebastian Ullrich
The State monad transformer using IO references. The State monad transformer using IO references.
-/ -/
prelude prelude
import Init.System.ST import Init.System.IO
import Init.Control.State
def StateRefT' (ω : Type) (σ : Type) (m : Type Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α def StateRefT' (ω : Type) (σ : Type) (m : Type Type) (α : Type) : Type := ReaderT (ST.Ref ω σ) m α
@@ -33,22 +34,22 @@ protected def lift (x : m α) : StateRefT' ω σ m α :=
instance [Monad m] : Monad (StateRefT' ω σ m) := inferInstanceAs (Monad (ReaderT _ _)) instance [Monad m] : Monad (StateRefT' ω σ m) := inferInstanceAs (Monad (ReaderT _ _))
instance : MonadLift m (StateRefT' ω σ m) := StateRefT'.lift instance : MonadLift m (StateRefT' ω σ m) := StateRefT'.lift
instance (σ m) : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _)) instance (σ m) [Monad m] : MonadFunctor m (StateRefT' ω σ m) := inferInstanceAs (MonadFunctor m (ReaderT _ _))
instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _)) instance [Alternative m] [Monad m] : Alternative (StateRefT' ω σ m) := inferInstanceAs (Alternative (ReaderT _ _))
@[inline] @[inline]
protected def get [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ := protected def get [Monad m] [MonadLiftT (ST ω) m] : StateRefT' ω σ m σ :=
fun ref => ref.get fun ref => ref.get
@[inline] @[inline]
protected def set [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit := protected def set [Monad m] [MonadLiftT (ST ω) m] (s : σ) : StateRefT' ω σ m PUnit :=
fun ref => ref.set s fun ref => ref.set s
@[inline] @[inline]
protected def modifyGet [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α := protected def modifyGet [Monad m] [MonadLiftT (ST ω) m] (f : σ α × σ) : StateRefT' ω σ m α :=
fun ref => ref.modifyGet f fun ref => ref.modifyGet f
instance [MonadLiftT (ST ω) m] : MonadStateOf σ (StateRefT' ω σ m) where instance [MonadLiftT (ST ω) m] [Monad m] : MonadStateOf σ (StateRefT' ω σ m) where
get := StateRefT'.get get := StateRefT'.get
set := StateRefT'.set set := StateRefT'.set
modifyGet := StateRefT'.modifyGet modifyGet := StateRefT'.modifyGet
@@ -63,5 +64,5 @@ end StateRefT'
instance (ω σ : Type) (m : Type Type) : MonadControl m (StateRefT' ω σ m) := instance (ω σ : Type) (m : Type Type) : MonadControl m (StateRefT' ω σ m) :=
inferInstanceAs (MonadControl m (ReaderT _ _)) inferInstanceAs (MonadControl m (ReaderT _ _))
instance {m : Type Type} {ω σ : Type} [MonadFinally m] : MonadFinally (StateRefT' ω σ m) := instance {m : Type Type} {ω σ : Type} [MonadFinally m] [Monad m] : MonadFinally (StateRefT' ω σ m) :=
inferInstanceAs (MonadFinally (ReaderT _ _)) inferInstanceAs (MonadFinally (ReaderT _ _))

View File

@@ -7,7 +7,6 @@ Notation for operators defined at Prelude.lean
-/ -/
prelude prelude
import Init.Tactics import Init.Tactics
import Init.Meta
namespace Lean.Parser.Tactic.Conv namespace Lean.Parser.Tactic.Conv
@@ -47,20 +46,12 @@ scoped syntax (name := withAnnotateState)
/-- `skip` does nothing. -/ /-- `skip` does nothing. -/
syntax (name := skip) "skip" : conv syntax (name := skip) "skip" : conv
/-- /-- Traverses into the left subterm of a binary operator.
Traverses into the left subterm of a binary operator. (In general, for an `n`-ary operator, it traverses into the second to last argument.) -/
In general, for an `n`-ary operator, it traverses into the second to last argument.
It is a synonym for `arg -2`.
-/
syntax (name := lhs) "lhs" : conv syntax (name := lhs) "lhs" : conv
/-- /-- Traverses into the right subterm of a binary operator.
Traverses into the right subterm of a binary operator. (In general, for an `n`-ary operator, it traverses into the last argument.) -/
In general, for an `n`-ary operator, it traverses into the last argument.
It is a synonym for `arg -1`.
-/
syntax (name := rhs) "rhs" : conv syntax (name := rhs) "rhs" : conv
/-- Traverses into the function of a (unary) function application. /-- Traverses into the function of a (unary) function application.
@@ -83,17 +74,13 @@ subgoals for all the function arguments. For example, if the target is `f x y` t
`congr` produces two subgoals, one for `x` and one for `y`. -/ `congr` produces two subgoals, one for `x` and one for `y`. -/
syntax (name := congr) "congr" : conv syntax (name := congr) "congr" : conv
syntax argArg := "@"? "-"? num
/-- /--
* `arg i` traverses into the `i`'th argument of the target. For example if the * `arg i` traverses into the `i`'th argument of the target. For example if the
target is `f a b c d` then `arg 1` traverses to `a` and `arg 3` traverses to `c`. target is `f a b c d` then `arg 1` traverses to `a` and `arg 3` traverses to `c`.
The index may be negative; `arg -1` traverses into the last argument,
`arg -2` into the second-to-last argument, and so on.
* `arg @i` is the same as `arg i` but it counts all arguments instead of just the * `arg @i` is the same as `arg i` but it counts all arguments instead of just the
explicit arguments. explicit arguments.
* `arg 0` traverses into the function. If the target is `f a b c d`, `arg 0` traverses into `f`. -/ * `arg 0` traverses into the function. If the target is `f a b c d`, `arg 0` traverses into `f`. -/
syntax (name := arg) "arg " argArg : conv syntax (name := arg) "arg " "@"? num : conv
/-- `ext x` traverses into a binder (a `fun x => e` or `∀ x, e` expression) /-- `ext x` traverses into a binder (a `fun x => e` or `∀ x, e` expression)
to target `e`, introducing name `x` in the process. -/ to target `e`, introducing name `x` in the process. -/
@@ -110,18 +97,11 @@ Users should prefer `unfold` for unfolding definitions. -/
syntax (name := delta) "delta" (ppSpace colGt ident)+ : conv syntax (name := delta) "delta" (ppSpace colGt ident)+ : conv
/-- /--
* `unfold id` unfolds all occurrences of definition `id` in the target. * `unfold foo` unfolds all occurrences of `foo` in the target.
* `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`. * `unfold id1 id2 ...` is equivalent to `unfold id1; unfold id2; ...`.
Like the `unfold` tactic, this uses equational lemmas for the chosen definition
Definitions can be either global or local definitions. to rewrite the target. For recursive definitions,
only one layer of unfolding is performed. -/
For non-recursive global definitions, this tactic is identical to `delta`.
For recursive global definitions, it uses the "unfolding lemma" `id.eq_def`,
which is generated for each recursive definition, to unfold according to the recursive definition given by the user.
Only one level of unfolding is performed, in contrast to `simp only [id]`, which unfolds definition `id` recursively.
This is the `conv` version of the `unfold` tactic.
-/
syntax (name := unfold) "unfold" (ppSpace colGt ident)+ : conv syntax (name := unfold) "unfold" (ppSpace colGt ident)+ : conv
/-- /--
@@ -143,11 +123,11 @@ For example, if we are searching for `f _` in `f (f a) = f b`:
syntax (name := pattern) "pattern " (occs)? term : conv syntax (name := pattern) "pattern " (occs)? term : conv
/-- `rw [thm]` rewrites the target using `thm`. See the `rw` tactic for more information. -/ /-- `rw [thm]` rewrites the target using `thm`. See the `rw` tactic for more information. -/
syntax (name := rewrite) "rewrite" optConfig rwRuleSeq : conv syntax (name := rewrite) "rewrite" (config)? rwRuleSeq : conv
/-- `simp [thm]` performs simplification using `thm` and marked `@[simp]` lemmas. /-- `simp [thm]` performs simplification using `thm` and marked `@[simp]` lemmas.
See the `simp` tactic for more information. -/ See the `simp` tactic for more information. -/
syntax (name := simp) "simp" optConfig (discharger)? (&" only")? syntax (name := simp) "simp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpStar <|> simpErase <|> simpLemma),*) "]")? : conv (" [" withoutPosition((simpStar <|> simpErase <|> simpLemma),*) "]")? : conv
/-- /--
@@ -164,7 +144,7 @@ example (a : Nat): (0 + 0) = a - a := by
rw [← Nat.sub_self a] rw [← Nat.sub_self a]
``` ```
-/ -/
syntax (name := dsimp) "dsimp" optConfig (discharger)? (&" only")? syntax (name := dsimp) "dsimp" (config)? (discharger)? (&" only")?
(" [" withoutPosition((simpErase <|> simpLemma),*) "]")? : conv (" [" withoutPosition((simpErase <|> simpLemma),*) "]")? : conv
/-- `simp_match` simplifies match expressions. For example, /-- `simp_match` simplifies match expressions. For example,
@@ -260,12 +240,12 @@ macro (name := failIfSuccess) tk:"fail_if_success " s:convSeq : conv =>
/-- `rw [rules]` applies the given list of rewrite rules to the target. /-- `rw [rules]` applies the given list of rewrite rules to the target.
See the `rw` tactic for more information. -/ See the `rw` tactic for more information. -/
macro "rw" c:optConfig s:rwRuleSeq : conv => `(conv| rewrite $c:optConfig $s) macro "rw" c:(config)? s:rwRuleSeq : conv => `(conv| rewrite $[$c]? $s)
/-- `erw [rules]` is a shorthand for `rw (transparency := .default) [rules]`. /-- `erw [rules]` is a shorthand for `rw (config := { transparency := .default }) [rules]`.
This does rewriting up to unfolding of regular definitions (by comparison to regular `rw` This does rewriting up to unfolding of regular definitions (by comparison to regular `rw`
which only unfolds `@[reducible]` definitions). -/ which only unfolds `@[reducible]` definitions). -/
macro "erw" c:optConfig s:rwRuleSeq : conv => `(conv| rw $[$(getConfigItems c)]* (transparency := .default) $s:rwRuleSeq) macro "erw" s:rwRuleSeq : conv => `(conv| rw (config := { transparency := .default }) $s)
/-- `args` traverses into all arguments. Synonym for `congr`. -/ /-- `args` traverses into all arguments. Synonym for `congr`. -/
macro "args" : conv => `(conv| congr) macro "args" : conv => `(conv| congr)
@@ -276,7 +256,7 @@ macro "right" : conv => `(conv| rhs)
/-- `intro` traverses into binders. Synonym for `ext`. -/ /-- `intro` traverses into binders. Synonym for `ext`. -/
macro "intro" xs:(ppSpace colGt ident)* : conv => `(conv| ext $xs*) macro "intro" xs:(ppSpace colGt ident)* : conv => `(conv| ext $xs*)
syntax enterArg := ident <|> argArg syntax enterArg := ident <|> ("@"? num)
/-- `enter [arg, ...]` is a compact way to describe a path to a subterm. /-- `enter [arg, ...]` is a compact way to describe a path to a subterm.
It is a shorthand for other conv tactics as follows: It is a shorthand for other conv tactics as follows:
@@ -285,7 +265,12 @@ It is a shorthand for other conv tactics as follows:
* `enter [x]` (where `x` is an identifier) is equivalent to `ext x`. * `enter [x]` (where `x` is an identifier) is equivalent to `ext x`.
For example, given the target `f (g a (fun x => x b))`, `enter [1, 2, x, 1]` For example, given the target `f (g a (fun x => x b))`, `enter [1, 2, x, 1]`
will traverse to the subterm `b`. -/ will traverse to the subterm `b`. -/
syntax (name := enter) "enter" " [" withoutPosition(enterArg,+) "]" : conv syntax "enter" " [" withoutPosition(enterArg,+) "]" : conv
macro_rules
| `(conv| enter [$i:num]) => `(conv| arg $i)
| `(conv| enter [@$i]) => `(conv| arg @$i)
| `(conv| enter [$id:ident]) => `(conv| ext $id)
| `(conv| enter [$arg, $args,*]) => `(conv| (enter [$arg]; enter [$args,*]))
/-- The `apply thm` conv tactic is the same as `apply thm` the tactic. /-- The `apply thm` conv tactic is the same as `apply thm` the tactic.
There are no restrictions on `thm`, but strange results may occur if `thm` There are no restrictions on `thm`, but strange results may occur if `thm`

View File

@@ -36,17 +36,6 @@ and `flip (·<·)` is the greater-than relation.
theorem Function.comp_def {α β δ} (f : β δ) (g : α β) : f g = fun x => f (g x) := rfl theorem Function.comp_def {α β δ} (f : β δ) (g : α β) : f g = fun x => f (g x) := rfl
@[simp] theorem Function.const_comp {f : α β} {c : γ} :
(Function.const β c f) = Function.const α c := by
rfl
@[simp] theorem Function.comp_const {f : β γ} {b : β} :
(f Function.const α b) = Function.const α (f b) := by
rfl
@[simp] theorem Function.true_comp {f : α β} : ((fun _ => true) f) = fun _ => true := by
rfl
@[simp] theorem Function.false_comp {f : α β} : ((fun _ => false) f) = fun _ => false := by
rfl
attribute [simp] namedPattern attribute [simp] namedPattern
/-- /--
@@ -165,23 +154,9 @@ inductive PSum (α : Sort u) (β : Sort v) where
@[inherit_doc] infixr:30 " ⊕' " => PSum @[inherit_doc] infixr:30 " ⊕' " => PSum
/-- instance {α β} [Inhabited α] : Inhabited (PSum α β) := PSum.inl default
`PSum α β` is inhabited if `α` is inhabited.
This is not an instance to avoid non-canonical instances.
-/
@[reducible] def PSum.inhabitedLeft {α β} [Inhabited α] : Inhabited (PSum α β) := PSum.inl default
/-- instance {α β} [Inhabited β] : Inhabited (PSum α β) := PSum.inr default
`PSum α β` is inhabited if `β` is inhabited.
This is not an instance to avoid non-canonical instances.
-/
@[reducible] def PSum.inhabitedRight {α β} [Inhabited β] : Inhabited (PSum α β) := PSum.inr default
instance PSum.nonemptyLeft [h : Nonempty α] : Nonempty (PSum α β) :=
Nonempty.elim h (fun a => PSum.inl a)
instance PSum.nonemptyRight [h : Nonempty β] : Nonempty (PSum α β) :=
Nonempty.elim h (fun b => PSum.inr b)
/-- /--
`Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs `Sigma β`, also denoted `Σ a : α, β a` or `(a : α) × β a`, is the type of dependent pairs
@@ -324,6 +299,7 @@ class ForIn' (m : Type u₁ → Type u₂) (ρ : Type u) (α : outParam (Type v)
export ForIn' (forIn') export ForIn' (forIn')
/-- /--
Auxiliary type used to compile `do` notation. It is used when compiling a do block Auxiliary type used to compile `do` notation. It is used when compiling a do block
nested inside a combinator like `tryCatch`. It encodes the possible ways the nested inside a combinator like `tryCatch`. It encodes the possible ways the
@@ -492,13 +468,11 @@ class Singleton (α : outParam <| Type u) (β : Type v) where
export Singleton (singleton) export Singleton (singleton)
/-- `insert x ∅ = {x}` -/ /-- `insert x ∅ = {x}` -/
class LawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert α β] [Singleton α β] : class IsLawfulSingleton (α : Type u) (β : Type v) [EmptyCollection β] [Insert α β] [Singleton α β] :
Prop where Prop where
/-- `insert x ∅ = {x}` -/ /-- `insert x ∅ = {x}` -/
insert_emptyc_eq (x : α) : (insert x : β) = singleton x insert_emptyc_eq (x : α) : (insert x : β) = singleton x
export LawfulSingleton (insert_emptyc_eq) export IsLawfulSingleton (insert_emptyc_eq)
attribute [simp] insert_emptyc_eq
/-- Type class used to implement the notation `{ a ∈ c | p a }` -/ /-- Type class used to implement the notation `{ a ∈ c | p a }` -/
class Sep (α : outParam <| Type u) (γ : Type v) where class Sep (α : outParam <| Type u) (γ : Type v) where
@@ -668,7 +642,7 @@ instance : LawfulBEq String := inferInstance
/-! # Logical connectives and equality -/ /-! # Logical connectives and equality -/
@[inherit_doc True.intro] theorem trivial : True := @[inherit_doc True.intro] def trivial : True :=
theorem mt {a b : Prop} (h₁ : a b) (h₂ : ¬b) : ¬a := theorem mt {a b : Prop} (h₁ : a b) (h₂ : ¬b) : ¬a :=
fun ha => h₂ (h₁ ha) fun ha => h₂ (h₁ ha)
@@ -727,7 +701,7 @@ theorem Ne.elim (h : a ≠ b) : a = b → False := h
theorem Ne.irrefl (h : a a) : False := h rfl theorem Ne.irrefl (h : a a) : False := h rfl
@[symm] theorem Ne.symm (h : a b) : b a := fun h₁ => h (h₁.symm) theorem Ne.symm (h : a b) : b a := fun h₁ => h (h₁.symm)
theorem ne_comm {α} {a b : α} : a b b a := Ne.symm, Ne.symm theorem ne_comm {α} {a b : α} : a b b a := Ne.symm, Ne.symm
@@ -780,7 +754,7 @@ noncomputable def HEq.elim {α : Sort u} {a : α} {p : α → Sort v} {b : α} (
theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b := theorem HEq.subst {p : (T : Sort u) T Prop} (h₁ : HEq a b) (h₂ : p α a) : p β b :=
HEq.ndrecOn h₁ h₂ HEq.ndrecOn h₁ h₂
@[symm] theorem HEq.symm (h : HEq a b) : HEq b a := theorem HEq.symm (h : HEq a b) : HEq b a :=
h.rec (HEq.refl a) h.rec (HEq.refl a)
theorem heq_of_eq (h : a = a') : HEq a a' := theorem heq_of_eq (h : a = a') : HEq a a' :=
@@ -813,16 +787,15 @@ theorem cast_heq {α β : Sort u} : (h : α = β) → (a : α) → HEq (cast h a
variable {a b c d : Prop} variable {a b c d : Prop}
theorem iff_iff_implies_and_implies {a b : Prop} : (a b) (a b) (b a) := theorem iff_iff_implies_and_implies (a b : Prop) : (a b) (a b) (b a) :=
Iff.intro (fun h => And.intro h.mp h.mpr) (fun h => Iff.intro h.left h.right) Iff.intro (fun h => And.intro h.mp h.mpr) (fun h => Iff.intro h.left h.right)
@[refl] theorem Iff.refl (a : Prop) : a a := theorem Iff.refl (a : Prop) : a a :=
Iff.intro (fun h => h) (fun h => h) Iff.intro (fun h => h) (fun h => h)
protected theorem Iff.rfl {a : Prop} : a a := protected theorem Iff.rfl {a : Prop} : a a :=
Iff.refl a Iff.refl a
-- And, also for backward compatibility, we try `Iff.rfl.` using `exact` (see #5366)
macro_rules | `(tactic| rfl) => `(tactic| exact Iff.rfl) macro_rules | `(tactic| rfl) => `(tactic| exact Iff.rfl)
theorem Iff.of_eq (h : a = b) : a b := h Iff.rfl theorem Iff.of_eq (h : a = b) : a b := h Iff.rfl
@@ -837,18 +810,15 @@ instance : Trans Iff Iff Iff where
theorem Eq.comm {a b : α} : a = b b = a := Iff.intro Eq.symm Eq.symm theorem Eq.comm {a b : α} : a = b b = a := Iff.intro Eq.symm Eq.symm
theorem eq_comm {a b : α} : a = b b = a := Eq.comm theorem eq_comm {a b : α} : a = b b = a := Eq.comm
theorem HEq.comm {a : α} {b : β} : HEq a b HEq b a := Iff.intro HEq.symm HEq.symm theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
theorem heq_comm {a : α} {b : β} : HEq a b HEq b a := HEq.comm
@[symm] theorem Iff.symm (h : a b) : b a := Iff.intro h.mpr h.mp
theorem Iff.comm: (a b) (b a) := Iff.intro Iff.symm Iff.symm theorem Iff.comm: (a b) (b a) := Iff.intro Iff.symm Iff.symm
theorem iff_comm : (a b) (b a) := Iff.comm theorem iff_comm : (a b) (b a) := Iff.comm
@[symm] theorem And.symm : a b b a := fun ha, hb => hb, ha theorem And.symm : a b b a := fun ha, hb => hb, ha
theorem And.comm : a b b a := Iff.intro And.symm And.symm theorem And.comm : a b b a := Iff.intro And.symm And.symm
theorem and_comm : a b b a := And.comm theorem and_comm : a b b a := And.comm
@[symm] theorem Or.symm : a b b a := .rec .inr .inl theorem Or.symm : a b b a := .rec .inr .inl
theorem Or.comm : a b b a := Iff.intro Or.symm Or.symm theorem Or.comm : a b b a := Iff.intro Or.symm Or.symm
theorem or_comm : a b b a := Or.comm theorem or_comm : a b b a := Or.comm
@@ -861,21 +831,16 @@ theorem Exists.elim {α : Sort u} {p : α → Prop} {b : Prop}
/-! # Decidable -/ /-! # Decidable -/
@[simp] theorem decide_true (h : Decidable True) : @decide True h = true := theorem decide_true_eq_true (h : Decidable True) : @decide True h = true :=
match h with match h with
| isTrue _ => rfl | isTrue _ => rfl
| isFalse h => False.elim <| h | isFalse h => False.elim <| h
@[simp] theorem decide_false (h : Decidable False) : @decide False h = false := theorem decide_false_eq_false (h : Decidable False) : @decide False h = false :=
match h with match h with
| isFalse _ => rfl | isFalse _ => rfl
| isTrue h => False.elim h | isTrue h => False.elim h
set_option linter.missingDocs false in
@[deprecated decide_true (since := "2024-11-05")] abbrev decide_true_eq_true := decide_true
set_option linter.missingDocs false in
@[deprecated decide_false (since := "2024-11-05")] abbrev decide_false_eq_false := decide_false
/-- Similar to `decide`, but uses an explicit instance -/ /-- Similar to `decide`, but uses an explicit instance -/
@[inline] def toBoolUsing {p : Prop} (d : Decidable p) : Bool := @[inline] def toBoolUsing {p : Prop} (d : Decidable p) : Bool :=
decide (h := d) decide (h := d)
@@ -918,7 +883,7 @@ theorem byContradiction [dec : Decidable p] (h : ¬p → False) : p :=
theorem of_not_not [Decidable p] : ¬ ¬ p p := theorem of_not_not [Decidable p] : ¬ ¬ p p :=
fun hnn => byContradiction (fun hn => absurd hn hnn) fun hnn => byContradiction (fun hn => absurd hn hnn)
theorem not_and_iff_or_not {p q : Prop} [d₁ : Decidable p] [d₂ : Decidable q] : ¬ (p q) ¬ p ¬ q := theorem not_and_iff_or_not (p q : Prop) [d₁ : Decidable p] [d₂ : Decidable q] : ¬ (p q) ¬ p ¬ q :=
Iff.intro Iff.intro
(fun h => match d₁, d₂ with (fun h => match d₁, d₂ with
| isTrue h₁, isTrue h₂ => absurd (And.intro h₁ h₂) h | isTrue h₁, isTrue h₂ => absurd (And.intro h₁ h₂) h
@@ -1124,30 +1089,19 @@ def InvImage {α : Sort u} {β : Sort v} (r : β → β → Prop) (f : α → β
fun a₁ a₂ => r (f a₁) (f a₂) fun a₁ a₂ => r (f a₁) (f a₂)
/-- /--
The transitive closure `TransGen r` of a relation `r` is the smallest relation which is The transitive closure `r` of a relation `r` is the smallest relation which is
transitive and contains `r`. `TransGen r a z` if and only if there exists a sequence transitive and contains `r`. `r a z` if and only if there exists a sequence
`a r b r ... r z` of length at least 1 connecting `a` to `z`. `a r b r ... r z` of length at least 1 connecting `a` to `z`.
-/ -/
inductive Relation.TransGen {α : Sort u} (r : α α Prop) : α α Prop inductive TC {α : Sort u} (r : α α Prop) : α α Prop where
/-- If `r a b` then `TransGen r a b`. This is the base case of the transitive closure. -/ /-- If `r a b` then `r a b`. This is the base case of the transitive closure. -/
| single {a b} : r a b TransGen r a b | base : a b, r a b TC r a b
/-- The transitive closure is transitive. -/ /-- The transitive closure is transitive. -/
| tail {a b c} : TransGen r a b r b c TransGen r a c | trans : a b c, TC r a b TC r b c TC r a c
/-- Deprecated synonym for `Relation.TransGen`. -/
@[deprecated Relation.TransGen (since := "2024-07-16")] abbrev TC := @Relation.TransGen
theorem Relation.TransGen.trans {α : Sort u} {r : α α Prop} {a b c} :
TransGen r a b TransGen r b c TransGen r a c := by
intro hab hbc
induction hbc with
| single h => exact TransGen.tail hab h
| tail _ h ih => exact TransGen.tail ih h
/-! # Subtype -/ /-! # Subtype -/
namespace Subtype namespace Subtype
theorem existsOfSubtype {α : Type u} {p : α Prop} : { x // p x } Exists (fun x => p x) theorem existsOfSubtype {α : Type u} {p : α Prop} : { x // p x } Exists (fun x => p x)
| a, h => a, h | a, h => a, h
@@ -1172,20 +1126,12 @@ end Subtype
section section
variable {α : Type u} {β : Type v} variable {α : Type u} {β : Type v}
/-- This is not an instance to avoid non-canonical instances. -/ instance Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
@[reducible] def Sum.inhabitedLeft [Inhabited α] : Inhabited (Sum α β) where
default := Sum.inl default default := Sum.inl default
/-- This is not an instance to avoid non-canonical instances. -/ instance Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
@[reducible] def Sum.inhabitedRight [Inhabited β] : Inhabited (Sum α β) where
default := Sum.inr default default := Sum.inr default
instance Sum.nonemptyLeft [h : Nonempty α] : Nonempty (Sum α β) :=
Nonempty.elim h (fun a => Sum.inl a)
instance Sum.nonemptyRight [h : Nonempty β] : Nonempty (Sum α β) :=
Nonempty.elim h (fun b => Sum.inr b)
instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b => instance {α : Type u} {β : Type v} [DecidableEq α] [DecidableEq β] : DecidableEq (Sum α β) := fun a b =>
match a, b with match a, b with
| Sum.inl a, Sum.inl b => | Sum.inl a, Sum.inl b =>
@@ -1201,21 +1147,6 @@ end
/-! # Product -/ /-! # Product -/
instance [h1 : Nonempty α] [h2 : Nonempty β] : Nonempty (α × β) :=
Nonempty.elim h1 fun x =>
Nonempty.elim h2 fun y =>
(x, y)
instance [h1 : Nonempty α] [h2 : Nonempty β] : Nonempty (MProd α β) :=
Nonempty.elim h1 fun x =>
Nonempty.elim h2 fun y =>
x, y
instance [h1 : Nonempty α] [h2 : Nonempty β] : Nonempty (PProd α β) :=
Nonempty.elim h1 fun x =>
Nonempty.elim h2 fun y =>
x, y
instance [Inhabited α] [Inhabited β] : Inhabited (α × β) where instance [Inhabited α] [Inhabited β] : Inhabited (α × β) where
default := (default, default) default := (default, default)
@@ -1242,7 +1173,7 @@ def Prod.lexLt [LT α] [LT β] (s : α × β) (t : α × β) : Prop :=
s.1 < t.1 (s.1 = t.1 s.2 < t.2) s.1 < t.1 (s.1 = t.1 s.2 < t.2)
instance Prod.lexLtDec instance Prod.lexLtDec
[LT α] [LT β] [DecidableEq α] [LT α] [LT β] [DecidableEq α] [DecidableEq β]
[(a b : α) Decidable (a < b)] [(a b : β) Decidable (a < b)] [(a b : α) Decidable (a < b)] [(a b : β) Decidable (a < b)]
: (s t : α × β) Decidable (Prod.lexLt s t) := : (s t : α × β) Decidable (Prod.lexLt s t) :=
fun _ _ => inferInstanceAs (Decidable (_ _)) fun _ _ => inferInstanceAs (Decidable (_ _))
@@ -1260,20 +1191,11 @@ def Prod.map {α₁ : Type u₁} {α₂ : Type u₂} {β₁ : Type v₁} {β₂
(f : α₁ α₂) (g : β₁ β₂) : α₁ × β₁ α₂ × β₂ (f : α₁ α₂) (g : β₁ β₂) : α₁ × β₁ α₂ × β₂
| (a, b) => (f a, g b) | (a, b) => (f a, g b)
@[simp] theorem Prod.map_apply (f : α β) (g : γ δ) (x) (y) :
Prod.map f g (x, y) = (f x, g y) := rfl
@[simp] theorem Prod.map_fst (f : α β) (g : γ δ) (x) : (Prod.map f g x).1 = f x.1 := rfl
@[simp] theorem Prod.map_snd (f : α β) (g : γ δ) (x) : (Prod.map f g x).2 = g x.2 := rfl
/-! # Dependent products -/ /-! # Dependent products -/
theorem Exists.of_psigma_prop {α : Sort u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x) theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x)
| x, hx => x, hx | x, hx => x, hx
@[deprecated Exists.of_psigma_prop (since := "2024-07-27")]
theorem ex_of_PSigma {α : Type u} {p : α Prop} : (PSigma (fun x => p x)) Exists (fun x => p x) :=
Exists.of_psigma_prop
protected theorem PSigma.eta {α : Sort u} {β : α Sort v} {a₁ a₂ : α} {b₁ : β a₁} {b₂ : β a₂} protected theorem PSigma.eta {α : Sort u} {β : α Sort v} {a₁ a₂ : α} {b₁ : β a₁} {b₂ : β a₂}
(h₁ : a₁ = a₂) (h₂ : Eq.ndrec b₁ h₁ = b₂) : PSigma.mk a₁ b₁ = PSigma.mk a₂ b₂ := by (h₁ : a₁ = a₂) (h₂ : Eq.ndrec b₁ h₁ = b₂) : PSigma.mk a₁ b₁ = PSigma.mk a₂ b₂ := by
subst h₁ subst h₁
@@ -1389,7 +1311,6 @@ gen_injective_theorems% Except
gen_injective_theorems% EStateM.Result gen_injective_theorems% EStateM.Result
gen_injective_theorems% Lean.Name gen_injective_theorems% Lean.Name
gen_injective_theorems% Lean.Syntax gen_injective_theorems% Lean.Syntax
gen_injective_theorems% BitVec
theorem Nat.succ.inj {m n : Nat} : m.succ = n.succ m = n := theorem Nat.succ.inj {m n : Nat} : m.succ = n.succ m = n :=
fun x => Nat.noConfusion x id fun x => Nat.noConfusion x id
@@ -1397,7 +1318,7 @@ theorem Nat.succ.inj {m n : Nat} : m.succ = n.succ → m = n :=
theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) := theorem Nat.succ.injEq (u v : Nat) : (u.succ = v.succ) = (u = v) :=
Eq.propIntro Nat.succ.inj (congrArg Nat.succ) Eq.propIntro Nat.succ.inj (congrArg Nat.succ)
@[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] {a b : α} : a == b a = b := @[simp] theorem beq_iff_eq [BEq α] [LawfulBEq α] (a b : α) : a == b a = b :=
eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl eq_of_beq, by intro h; subst h; exact LawfulBEq.rfl
/-! # Prop lemmas -/ /-! # Prop lemmas -/
@@ -1436,9 +1357,6 @@ theorem iff_false_right (ha : ¬a) : (b ↔ a) ↔ ¬b := Iff.comm.trans (iff_fa
theorem of_iff_true (h : a True) : a := h.mpr trivial theorem of_iff_true (h : a True) : a := h.mpr trivial
theorem iff_true_intro (h : a) : a True := iff_of_true h trivial theorem iff_true_intro (h : a) : a True := iff_of_true h trivial
theorem eq_iff_true_of_subsingleton [Subsingleton α] (x y : α) : x = y True :=
iff_true_intro (Subsingleton.elim ..)
theorem not_of_iff_false : (p False) ¬p := Iff.mp theorem not_of_iff_false : (p False) ¬p := Iff.mp
theorem iff_false_intro (h : ¬a) : a False := iff_of_false h id theorem iff_false_intro (h : ¬a) : a False := iff_of_false h id
@@ -1462,7 +1380,7 @@ theorem false_of_true_eq_false (h : True = False) : False := false_of_true_iff_
theorem true_eq_false_of_false : False (True = False) := False.elim theorem true_eq_false_of_false : False (True = False) := False.elim
theorem iff_def : (a b) (a b) (b a) := iff_iff_implies_and_implies theorem iff_def : (a b) (a b) (b a) := iff_iff_implies_and_implies a b
theorem iff_def' : (a b) (b a) (a b) := Iff.trans iff_def And.comm theorem iff_def' : (a b) (b a) (a b) := Iff.trans iff_def And.comm
theorem true_iff_false : (True False) False := iff_false_intro (·.mp True.intro) theorem true_iff_false : (True False) False := iff_false_intro (·.mp True.intro)
@@ -1490,7 +1408,7 @@ theorem imp_true_iff (α : Sort u) : (α → True) ↔ True := iff_true_intro (f
theorem false_imp_iff (a : Prop) : (False a) True := iff_true_intro False.elim theorem false_imp_iff (a : Prop) : (False a) True := iff_true_intro False.elim
theorem true_imp_iff {α : Prop} : (True α) α := imp_iff_right True.intro theorem true_imp_iff (α : Prop) : (True α) α := imp_iff_right True.intro
@[simp high] theorem imp_self : (a a) True := iff_true_intro id @[simp high] theorem imp_self : (a a) True := iff_true_intro id
@@ -1610,13 +1528,13 @@ so you should consider the simpler versions if they apply:
* `Quot.recOnSubsingleton`, when the target type is a `Subsingleton` * `Quot.recOnSubsingleton`, when the target type is a `Subsingleton`
* `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption * `Quot.hrecOn`, which uses `HEq (f a) (f b)` instead of a `sound p ▸ f a = f b` assummption
-/ -/
@[elab_as_elim] protected abbrev rec protected abbrev rec
(f : (a : α) motive (Quot.mk r a)) (f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b) (h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
(q : Quot r) : motive q := (q : Quot r) : motive q :=
Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2) Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((lift (Quot.indep f) (Quot.indepCoherent f h) q).2)
@[inherit_doc Quot.rec, elab_as_elim] protected abbrev recOn @[inherit_doc Quot.rec] protected abbrev recOn
(q : Quot r) (q : Quot r)
(f : (a : α) motive (Quot.mk r a)) (f : (a : α) motive (Quot.mk r a))
(h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b) (h : (a b : α) (p : r a b) Eq.ndrec (f a) (sound p) = f b)
@@ -1627,7 +1545,7 @@ so you should consider the simpler versions if they apply:
Dependent induction principle for a quotient, when the target type is a `Subsingleton`. Dependent induction principle for a quotient, when the target type is a `Subsingleton`.
In this case the quotient's side condition is trivial so any function can be lifted. In this case the quotient's side condition is trivial so any function can be lifted.
-/ -/
@[elab_as_elim] protected abbrev recOnSubsingleton protected abbrev recOnSubsingleton
[h : (a : α) Subsingleton (motive (Quot.mk r a))] [h : (a : α) Subsingleton (motive (Quot.mk r a))]
(q : Quot r) (q : Quot r)
(f : (a : α) motive (Quot.mk r a)) (f : (a : α) motive (Quot.mk r a))
@@ -1696,7 +1614,7 @@ protected theorem ind {α : Sort u} {s : Setoid α} {motive : Quotient s → Pro
/-- /--
The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`, The analogue of `Quot.liftOn`: if `f : α → β` respects the equivalence relation `≈`,
then it lifts to a function on `Quotient s` such that `liftOn (mk a) f h = f a`. then it lifts to a function on `Quotient s` such that `lift (mk a) f h = f a`.
-/ -/
protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α β) (c : (a b : α) a b f a = f b) : β := protected abbrev liftOn {α : Sort u} {β : Sort v} {s : Setoid α} (q : Quotient s) (f : α β) (c : (a b : α) a b f a = f b) : β :=
Quot.liftOn q f c Quot.liftOn q f c
@@ -1869,8 +1787,7 @@ section
variable {α : Type u} variable {α : Type u}
variable (r : α α Prop) variable (r : α α Prop)
instance Quotient.decidableEq {α : Sort u} {s : Setoid α} [d : (a b : α), Decidable (a b)] instance {α : Sort u} {s : Setoid α} [d : (a b : α), Decidable (a b)] : DecidableEq (Quotient s) :=
: DecidableEq (Quotient s) :=
fun (q₁ q₂ : Quotient s) => fun (q₁ q₂ : Quotient s) =>
Quotient.recOnSubsingleton₂ q₁ q₂ Quotient.recOnSubsingleton₂ q₁ q₂
fun a₁ a₂ => fun a₁ a₂ =>
@@ -1902,8 +1819,7 @@ theorem funext {α : Sort u} {β : α → Sort v} {f g : (x : α) → β x}
show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g) show extfunApp (Quot.mk eqv f) = extfunApp (Quot.mk eqv g)
exact congrArg extfunApp (Quot.sound h) exact congrArg extfunApp (Quot.sound h)
instance Pi.instSubsingleton {α : Sort u} {β : α Sort v} [ a, Subsingleton (β a)] : instance {α : Sort u} {β : α Sort v} [ a, Subsingleton (β a)] : Subsingleton ( a, β a) where
Subsingleton ( a, β a) where
allEq f g := funext fun a => Subsingleton.elim (f a) (g a) allEq f g := funext fun a => Subsingleton.elim (f a) (g a)
/-! # Squash -/ /-! # Squash -/
@@ -1922,12 +1838,12 @@ represents an element of `Squash α` the same as `α` itself
`Squash.lift` will extract a value in any subsingleton `β` from a function on `α`, `Squash.lift` will extract a value in any subsingleton `β` from a function on `α`,
while `Nonempty.rec` can only do the same when `β` is a proposition. while `Nonempty.rec` can only do the same when `β` is a proposition.
-/ -/
def Squash (α : Sort u) := Quot (fun (_ _ : α) => True) def Squash (α : Type u) := Quot (fun (_ _ : α) => True)
/-- The canonical quotient map into `Squash α`. -/ /-- The canonical quotient map into `Squash α`. -/
def Squash.mk {α : Sort u} (x : α) : Squash α := Quot.mk _ x def Squash.mk {α : Type u} (x : α) : Squash α := Quot.mk _ x
theorem Squash.ind {α : Sort u} {motive : Squash α Prop} (h : (a : α), motive (Squash.mk a)) : (q : Squash α), motive q := theorem Squash.ind {α : Type u} {motive : Squash α Prop} (h : (a : α), motive (Squash.mk a)) : (q : Squash α), motive q :=
Quot.ind h Quot.ind h
/-- If `β` is a subsingleton, then a function `α → β` lifts to `Squash α → β`. -/ /-- If `β` is a subsingleton, then a function `α → β` lifts to `Squash α → β`. -/
@@ -1941,6 +1857,15 @@ instance : Subsingleton (Squash α) where
apply Quot.sound apply Quot.sound
trivial trivial
/-! # Relations -/
/--
`Antisymm (·≤·)` says that `(·≤·)` is antisymmetric, that is, `a ≤ b → b ≤ a → a = b`.
-/
class Antisymm {α : Sort u} (r : α α Prop) where
/-- An antisymmetric relation `(·≤·)` satisfies `a ≤ b → b ≤ a → a = b`. -/
antisymm {a b : α} : r a b r b a a = b
namespace Lean namespace Lean
/-! # Kernel reduction hints -/ /-! # Kernel reduction hints -/
@@ -2057,7 +1982,7 @@ class IdempotentOp (op : ααα) : Prop where
`LeftIdentify op o` indicates `o` is a left identity of `op`. `LeftIdentify op o` indicates `o` is a left identity of `op`.
This class does not require a proof that `o` is an identity, and This class does not require a proof that `o` is an identity, and
is used primarily for inferring the identity using class resolution. is used primarily for infering the identity using class resoluton.
-/ -/
class LeftIdentity (op : α β β) (o : outParam α) : Prop class LeftIdentity (op : α β β) (o : outParam α) : Prop
@@ -2073,7 +1998,7 @@ class LawfulLeftIdentity (op : α → β → β) (o : outParam α) extends LeftI
`RightIdentify op o` indicates `o` is a right identity `o` of `op`. `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 This class does not require a proof that `o` is an identity, and is used
primarily for inferring the identity using class resolution. primarily for infering the identity using class resoluton.
-/ -/
class RightIdentity (op : α β α) (o : outParam β) : Prop class RightIdentity (op : α β α) (o : outParam β) : Prop
@@ -2089,7 +2014,7 @@ class LawfulRightIdentity (op : α → β → α) (o : outParam β) extends Righ
`Identity op o` indicates `o` is a left and right identity of `op`. `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 This class does not require a proof that `o` is an identity, and is used
primarily for inferring the identity using class resolution. primarily for infering the identity using class resoluton.
-/ -/
class Identity (op : α α α) (o : outParam α) extends LeftIdentity op o, RightIdentity op o : Prop class Identity (op : α α α) (o : outParam α) extends LeftIdentity op o, RightIdentity op o : Prop
@@ -2116,14 +2041,4 @@ instance : Commutative Or := ⟨fun _ _ => propext or_comm⟩
instance : Commutative And := fun _ _ => propext and_comm instance : Commutative And := fun _ _ => propext and_comm
instance : Commutative Iff := fun _ _ => propext iff_comm instance : Commutative Iff := fun _ _ => propext iff_comm
/--
`Antisymm (·≤·)` says that `(·≤·)` is antisymmetric, that is, `a ≤ b → b ≤ a → a = b`.
-/
class Antisymm (r : α α Prop) : Prop where
/-- An antisymmetric relation `(·≤·)` satisfies `a ≤ b → b ≤ a → a = b`. -/
antisymm {a b : α} : r a b r b a a = b
@[deprecated Antisymm (since := "2024-10-16"), inherit_doc Antisymm]
abbrev _root_.Antisymm (r : α α Prop) : Prop := Std.Antisymm r
end Std end Std

View File

@@ -19,9 +19,7 @@ import Init.Data.ByteArray
import Init.Data.FloatArray import Init.Data.FloatArray
import Init.Data.Fin import Init.Data.Fin
import Init.Data.UInt import Init.Data.UInt
import Init.Data.SInt
import Init.Data.Float import Init.Data.Float
import Init.Data.Float32
import Init.Data.Option import Init.Data.Option
import Init.Data.Ord import Init.Data.Ord
import Init.Data.Random import Init.Data.Random
@@ -35,13 +33,5 @@ import Init.Data.Prod
import Init.Data.AC import Init.Data.AC
import Init.Data.Queue import Init.Data.Queue
import Init.Data.Channel import Init.Data.Channel
import Init.Data.Cast
import Init.Data.Sum import Init.Data.Sum
import Init.Data.BEq
import Init.Data.Subtype
import Init.Data.ULift
import Init.Data.PLift
import Init.Data.Zero
import Init.Data.NeZero
import Init.Data.Function
import Init.Data.RArray
import Init.Data.Vector

View File

@@ -6,7 +6,7 @@ Authors: Dany Fabian
prelude prelude
import Init.Classical import Init.Classical
import Init.ByCases import Init.Data.List
namespace Lean.Data.AC namespace Lean.Data.AC
inductive Expr inductive Expr
@@ -146,8 +146,8 @@ theorem Context.evalList_mergeIdem (ctx : Context α) (h : ContextInformation.is
| nil => | nil =>
simp [mergeIdem, mergeIdem.loop] simp [mergeIdem, mergeIdem.loop]
split split
next h₂ => simp [evalList, h₂, h.1, EvalInformation.evalOp] case inl h₂ => simp [evalList, h₂, h.1, EvalInformation.evalOp]
next => rfl rfl
| cons z zs => | cons z zs =>
by_cases h₂ : x = y by_cases h₂ : x = y
case pos => case pos =>
@@ -191,11 +191,11 @@ theorem Context.evalList_insert
. simp [evalList, h.1, EvalInformation.evalOp] . simp [evalList, h.1, EvalInformation.evalOp]
| step y z zs ih => | step y z zs ih =>
simp [insert] at *; split simp [insert] at *; split
next => rfl case inl => rfl
next => case inr =>
split split
next => simp [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)] case inl => simp [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)]
next => simp_all [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)] case inr => simp_all [evalList, EvalInformation.evalOp]; rw [h.1, ctx.assoc.1, h.1 (evalList _ _ _)]
theorem Context.evalList_sort_congr theorem Context.evalList_sort_congr
(ctx : Context α) (ctx : Context α)
@@ -260,7 +260,7 @@ theorem Context.evalList_sort (ctx : Context α) (h : ContextInformation.isComm
simp [ContextInformation.isComm, Option.isSome] at h simp [ContextInformation.isComm, Option.isSome] at h
match h₂ : ctx.comm with match h₂ : ctx.comm with
| none => | none =>
simp [h₂] at h simp only [h₂] at h
| some val => | some val =>
simp [h₂] at h simp [h₂] at h
exact val.down exact val.down

View File

@@ -10,15 +10,5 @@ import Init.Data.Array.BinSearch
import Init.Data.Array.InsertionSort import Init.Data.Array.InsertionSort
import Init.Data.Array.DecidableEq import Init.Data.Array.DecidableEq
import Init.Data.Array.Mem import Init.Data.Array.Mem
import Init.Data.Array.Attach
import Init.Data.Array.BasicAux import Init.Data.Array.BasicAux
import Init.Data.Array.Lemmas import Init.Data.Array.Lemmas
import Init.Data.Array.TakeDrop
import Init.Data.Array.Bootstrap
import Init.Data.Array.GetLit
import Init.Data.Array.MapIdx
import Init.Data.Array.Set
import Init.Data.Array.Monadic
import Init.Data.Array.FinRange
import Init.Data.Array.Perm
import Init.Data.Array.Find

View File

@@ -1,583 +0,0 @@
/-
Copyright (c) 2021 Floris van Doorn. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joachim Breitner, Mario Carneiro
-/
prelude
import Init.Data.Array.Mem
import Init.Data.Array.Lemmas
import Init.Data.List.Attach
namespace Array
/--
`O(n)`. Partial map. If `f : Π a, P a → β` is a partial function defined on
`a : α` satisfying `P`, then `pmap f l h` is essentially the same as `map f l`
but is defined only when all members of `l` satisfy `P`, using the proof
to apply `f`.
We replace this at runtime with a more efficient version via the `csimp` lemma `pmap_eq_pmapImpl`.
-/
def pmap {P : α Prop} (f : a, P a β) (l : Array α) (H : a l, P a) : Array β :=
(l.toList.pmap f (fun a m => H a (mem_def.mpr m))).toArray
/--
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
`Array {x // P x}` is the same as the input `Array α`.
-/
@[inline] private unsafe def attachWithImpl
(xs : Array α) (P : α Prop) (_ : x xs, P x) : Array {x // P x} := unsafeCast xs
/-- `O(1)`. "Attach" a proof `P x` that holds for all the elements of `xs` to produce a new array
with the same elements but in the type `{x // P x}`. -/
@[implemented_by attachWithImpl] def attachWith
(xs : Array α) (P : α Prop) (H : x xs, P x) : Array {x // P x} :=
xs.toList.attachWith P fun x h => H x (Array.Mem.mk h)
/-- `O(1)`. "Attach" the proof that the elements of `xs` are in `xs` to produce a new array
with the same elements but in the type `{x // x ∈ xs}`. -/
@[inline] def attach (xs : Array α) : Array {x // x xs} := xs.attachWith _ fun _ => id
@[simp] theorem _root_.List.attachWith_toArray {l : List α} {P : α Prop} {H : x l.toArray, P x} :
l.toArray.attachWith P H = (l.attachWith P (by simpa using H)).toArray := by
simp [attachWith]
@[simp] theorem _root_.List.attach_toArray {l : List α} :
l.toArray.attach = (l.attachWith (· l.toArray) (by simp)).toArray := by
simp [attach]
@[simp] theorem _root_.List.pmap_toArray {l : List α} {P : α Prop} {f : a, P a β} {H : a l.toArray, P a} :
l.toArray.pmap f H = (l.pmap f (by simpa using H)).toArray := by
simp [pmap]
@[simp] theorem toList_attachWith {l : Array α} {P : α Prop} {H : x l, P x} :
(l.attachWith P H).toList = l.toList.attachWith P (by simpa [mem_toList] using H) := by
simp [attachWith]
@[simp] theorem toList_attach {α : Type _} {l : Array α} :
l.attach.toList = l.toList.attachWith (· l) (by simp [mem_toList]) := by
simp [attach]
@[simp] theorem toList_pmap {l : Array α} {P : α Prop} {f : a, P a β} {H : a l, P a} :
(l.pmap f H).toList = l.toList.pmap f (fun a m => H a (mem_def.mpr m)) := by
simp [pmap]
/-- Implementation of `pmap` using the zero-copy version of `attach`. -/
@[inline] private def pmapImpl {P : α Prop} (f : a, P a β) (l : Array α) (H : a l, P a) :
Array β := (l.attachWith _ H).map fun x, h' => f x h'
@[csimp] private theorem pmap_eq_pmapImpl : @pmap = @pmapImpl := by
funext α β p f L h'
cases L
simp only [pmap, pmapImpl, List.attachWith_toArray, List.map_toArray, mk.injEq, List.map_attachWith]
apply List.pmap_congr_left
intro a m h₁ h₂
congr
@[simp] theorem pmap_empty {P : α Prop} (f : a, P a β) : pmap f #[] (by simp) = #[] := rfl
@[simp] theorem pmap_push {P : α Prop} (f : a, P a β) (a : α) (l : Array α) (h : b l.push a, P b) :
pmap f (l.push a) h =
(pmap f l (fun a m => by simp at h; exact h a (.inl m))).push (f a (h a (by simp))) := by
simp [pmap]
@[simp] theorem attach_empty : (#[] : Array α).attach = #[] := rfl
@[simp] theorem attachWith_empty {P : α Prop} (H : x #[], P x) : (#[] : Array α).attachWith P H = #[] := rfl
@[simp] theorem _root_.List.attachWith_mem_toArray {l : List α} :
l.attachWith (fun x => x l.toArray) (fun x h => by simpa using h) =
l.attach.map fun x, h => x, by simpa using h := by
simp only [List.attachWith, List.attach, List.map_pmap]
apply List.pmap_congr_left
simp
@[simp]
theorem pmap_eq_map (p : α Prop) (f : α β) (l : Array α) (H) :
@pmap _ _ p (fun a _ => f a) l H = map f l := by
cases l; simp
theorem pmap_congr_left {p q : α Prop} {f : a, p a β} {g : a, q a β} (l : Array α) {H₁ H₂}
(h : a l, (h₁ h₂), f a h₁ = g a h₂) : pmap f l H₁ = pmap g l H₂ := by
cases l
simp only [mem_toArray] at h
simp only [List.pmap_toArray, mk.injEq]
rw [List.pmap_congr_left _ h]
theorem map_pmap {p : α Prop} (g : β γ) (f : a, p a β) (l H) :
map g (pmap f l H) = pmap (fun a h => g (f a h)) l H := by
cases l
simp [List.map_pmap]
theorem pmap_map {p : β Prop} (g : b, p b γ) (f : α β) (l H) :
pmap g (map f l) H = pmap (fun a h => g (f a) h) l fun _ h => H _ (mem_map_of_mem _ h) := by
cases l
simp [List.pmap_map]
theorem attach_congr {l₁ l₂ : Array α} (h : l₁ = l₂) :
l₁.attach = l₂.attach.map (fun x => x.1, h x.2) := by
subst h
simp
theorem attachWith_congr {l₁ l₂ : Array α} (w : l₁ = l₂) {P : α Prop} {H : x l₁, P x} :
l₁.attachWith P H = l₂.attachWith P fun _ h => H _ (w h) := by
subst w
simp
@[simp] theorem attach_push {a : α} {l : Array α} :
(l.push a).attach =
(l.attach.map (fun x, h => x, mem_push_of_mem a h)).push a, by simp := by
cases l
rw [attach_congr (List.push_toArray _ _)]
simp [Function.comp_def]
@[simp] theorem attachWith_push {a : α} {l : Array α} {P : α Prop} {H : x l.push a, P x} :
(l.push a).attachWith P H =
(l.attachWith P (fun x h => by simp at H; exact H x (.inl h))).push a, H a (by simp) := by
cases l
simp [attachWith_congr (List.push_toArray _ _)]
theorem pmap_eq_map_attach {p : α Prop} (f : a, p a β) (l H) :
pmap f l H = l.attach.map fun x => f x.1 (H _ x.2) := by
cases l
simp [List.pmap_eq_map_attach]
theorem attach_map_coe (l : Array α) (f : α β) :
(l.attach.map fun (i : {i // i l}) => f i) = l.map f := by
cases l
simp [List.attach_map_coe]
theorem attach_map_val (l : Array α) (f : α β) : (l.attach.map fun i => f i.val) = l.map f :=
attach_map_coe _ _
@[simp]
theorem attach_map_subtype_val (l : Array α) : l.attach.map Subtype.val = l := by
cases l; simp
theorem attachWith_map_coe {p : α Prop} (f : α β) (l : Array α) (H : a l, p a) :
((l.attachWith p H).map fun (i : { i // p i}) => f i) = l.map f := by
cases l; simp
theorem attachWith_map_val {p : α Prop} (f : α β) (l : Array α) (H : a l, p a) :
((l.attachWith p H).map fun i => f i.val) = l.map f :=
attachWith_map_coe _ _ _
@[simp]
theorem attachWith_map_subtype_val {p : α Prop} (l : Array α) (H : a l, p a) :
(l.attachWith p H).map Subtype.val = l := by
cases l; simp
@[simp]
theorem mem_attach (l : Array α) : x, x l.attach
| a, h => by
have := mem_map.1 (by rw [attach_map_subtype_val] <;> exact h)
rcases this with _, _, m, rfl
exact m
@[simp]
theorem mem_pmap {p : α Prop} {f : a, p a β} {l H b} :
b pmap f l H (a : _) (h : a l), f a (H a h) = b := by
simp only [pmap_eq_map_attach, mem_map, mem_attach, true_and, Subtype.exists, eq_comm]
theorem mem_pmap_of_mem {p : α Prop} {f : a, p a β} {l H} {a} (h : a l) :
f a (H a h) pmap f l H := by
rw [mem_pmap]
exact a, h, rfl
@[simp]
theorem size_pmap {p : α Prop} {f : a, p a β} {l H} : (pmap f l H).size = l.size := by
cases l; simp
@[simp]
theorem size_attach {L : Array α} : L.attach.size = L.size := by
cases L; simp
@[simp]
theorem size_attachWith {p : α Prop} {l : Array α} {H} : (l.attachWith p H).size = l.size := by
cases l; simp
@[simp]
theorem pmap_eq_empty_iff {p : α Prop} {f : a, p a β} {l H} : pmap f l H = #[] l = #[] := by
cases l; simp
theorem pmap_ne_empty_iff {P : α Prop} (f : (a : α) P a β) {xs : Array α}
(H : (a : α), a xs P a) : xs.pmap f H #[] xs #[] := by
cases xs; simp
theorem pmap_eq_self {l : Array α} {p : α Prop} (hp : (a : α), a l p a)
(f : (a : α) p a α) : l.pmap f hp = l a (h : a l), f a (hp a h) = a := by
cases l; simp [List.pmap_eq_self]
@[simp]
theorem attach_eq_empty_iff {l : Array α} : l.attach = #[] l = #[] := by
cases l; simp
theorem attach_ne_empty_iff {l : Array α} : l.attach #[] l #[] := by
cases l; simp
@[simp]
theorem attachWith_eq_empty_iff {l : Array α} {P : α Prop} {H : a l, P a} :
l.attachWith P H = #[] l = #[] := by
cases l; simp
theorem attachWith_ne_empty_iff {l : Array α} {P : α Prop} {H : a l, P a} :
l.attachWith P H #[] l #[] := by
cases l; simp
@[simp]
theorem getElem?_pmap {p : α Prop} (f : a, p a β) {l : Array α} (h : a l, p a) (n : Nat) :
(pmap f l h)[n]? = Option.pmap f l[n]? fun x H => h x (mem_of_getElem? H) := by
cases l; simp
@[simp]
theorem getElem_pmap {p : α Prop} (f : a, p a β) {l : Array α} (h : a l, p a) {n : Nat}
(hn : n < (pmap f l h).size) :
(pmap f l h)[n] =
f (l[n]'(@size_pmap _ _ p f l h hn))
(h _ (getElem_mem (@size_pmap _ _ p f l h hn))) := by
cases l; simp
@[simp]
theorem getElem?_attachWith {xs : Array α} {i : Nat} {P : α Prop} {H : a xs, P a} :
(xs.attachWith P H)[i]? = xs[i]?.pmap Subtype.mk (fun _ a => H _ (mem_of_getElem? a)) :=
getElem?_pmap ..
@[simp]
theorem getElem?_attach {xs : Array α} {i : Nat} :
xs.attach[i]? = xs[i]?.pmap Subtype.mk (fun _ a => mem_of_getElem? a) :=
getElem?_attachWith
@[simp]
theorem getElem_attachWith {xs : Array α} {P : α Prop} {H : a xs, P a}
{i : Nat} (h : i < (xs.attachWith P H).size) :
(xs.attachWith P H)[i] = xs[i]'(by simpa using h), H _ (getElem_mem (by simpa using h)) :=
getElem_pmap _ _ h
@[simp]
theorem getElem_attach {xs : Array α} {i : Nat} (h : i < xs.attach.size) :
xs.attach[i] = xs[i]'(by simpa using h), getElem_mem (by simpa using h) :=
getElem_attachWith h
theorem foldl_pmap (l : Array α) {P : α Prop} (f : (a : α) P a β)
(H : (a : α), a l P a) (g : γ β γ) (x : γ) :
(l.pmap f H).foldl g x = l.attach.foldl (fun acc a => g acc (f a.1 (H _ a.2))) x := by
rw [pmap_eq_map_attach, foldl_map]
theorem foldr_pmap (l : Array α) {P : α Prop} (f : (a : α) P a β)
(H : (a : α), a l P a) (g : β γ γ) (x : γ) :
(l.pmap f H).foldr g x = l.attach.foldr (fun a acc => g (f a.1 (H _ a.2)) acc) x := by
rw [pmap_eq_map_attach, foldr_map]
/--
If we fold over `l.attach` with a function that ignores the membership predicate,
we get the same results as folding over `l` directly.
This is useful when we need to use `attach` to show termination.
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
and even when rewriting we need to specify the function explicitly.
See however `foldl_subtype` below.
-/
theorem foldl_attach (l : Array α) (f : β α β) (b : β) :
l.attach.foldl (fun acc t => f acc t.1) b = l.foldl f b := by
rcases l with l
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.map_attach, size_toArray,
List.length_pmap, List.foldl_toArray', mem_toArray, List.foldl_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
/--
If we fold over `l.attach` with a function that ignores the membership predicate,
we get the same results as folding over `l` directly.
This is useful when we need to use `attach` to show termination.
Unfortunately this can't be applied by `simp` because of the higher order unification problem,
and even when rewriting we need to specify the function explicitly.
See however `foldr_subtype` below.
-/
theorem foldr_attach (l : Array α) (f : α β β) (b : β) :
l.attach.foldr (fun t acc => f t.1 acc) b = l.foldr f b := by
rcases l with l
simp only [List.attach_toArray, List.attachWith_mem_toArray, List.map_attach, size_toArray,
List.length_pmap, List.foldr_toArray', mem_toArray, List.foldr_subtype]
congr
ext
simpa using fun a => List.mem_of_getElem? a
theorem attach_map {l : Array α} (f : α β) :
(l.map f).attach = l.attach.map (fun x, h => f x, mem_map_of_mem f h) := by
cases l
ext <;> simp
theorem attachWith_map {l : Array α} (f : α β) {P : β Prop} {H : (b : β), b l.map f P b} :
(l.map f).attachWith P H = (l.attachWith (P f) (fun _ h => H _ (mem_map_of_mem f h))).map
fun x, h => f x, h := by
cases l
ext
· simp
· simp only [List.map_toArray, List.attachWith_toArray, List.getElem_toArray,
List.getElem_attachWith, List.getElem_map, Function.comp_apply]
erw [List.getElem_attachWith] -- Why is `erw` needed here?
theorem map_attachWith {l : Array α} {P : α Prop} {H : (a : α), a l P a}
(f : { x // P x } β) :
(l.attachWith P H).map f =
l.pmap (fun a (h : a l P a) => f a, H _ h.1) (fun a h => h, H a h) := by
cases l
ext <;> simp
/-- See also `pmap_eq_map_attach` for writing `pmap` in terms of `map` and `attach`. -/
theorem map_attach {l : Array α} (f : { x // x l } β) :
l.attach.map f = l.pmap (fun a h => f a, h) (fun _ => id) := by
cases l
ext <;> simp
theorem attach_filterMap {l : Array α} {f : α Option β} :
(l.filterMap f).attach = l.attach.filterMap
fun x, h => (f x).pbind (fun b m => some b, mem_filterMap.mpr x, h, m) := by
cases l
rw [attach_congr (List.filterMap_toArray f _)]
simp [List.attach_filterMap, List.map_filterMap, Function.comp_def]
theorem attach_filter {l : Array α} (p : α Bool) :
(l.filter p).attach = l.attach.filterMap
fun x => if w : p x.1 then some x.1, mem_filter.mpr x.2, w else none := by
cases l
rw [attach_congr (List.filter_toArray p _)]
simp [List.attach_filter, List.map_filterMap, Function.comp_def]
-- We are still missing here `attachWith_filterMap` and `attachWith_filter`.
-- Also missing are `filterMap_attach`, `filter_attach`, `filterMap_attachWith` and `filter_attachWith`.
theorem pmap_pmap {p : α Prop} {q : β Prop} (g : a, p a β) (f : b, q b γ) (l H₁ H₂) :
pmap f (pmap g l H₁) H₂ =
pmap (α := { x // x l }) (fun a h => f (g a h) (H₂ (g a h) (mem_pmap_of_mem a.2))) l.attach
(fun a _ => H₁ a a.2) := by
cases l
simp [List.pmap_pmap, List.pmap_map]
@[simp] theorem pmap_append {p : ι Prop} (f : a : ι, p a α) (l₁ l₂ : Array ι)
(h : a l₁ ++ l₂, p a) :
(l₁ ++ l₂).pmap f h =
(l₁.pmap f fun a ha => h a (mem_append_left l₂ ha)) ++
l₂.pmap f fun a ha => h a (mem_append_right l₁ ha) := by
cases l₁
cases l₂
simp
theorem pmap_append' {p : α Prop} (f : a : α, p a β) (l₁ l₂ : Array α)
(h₁ : a l₁, p a) (h₂ : a l₂, p a) :
((l₁ ++ l₂).pmap f fun a ha => (mem_append.1 ha).elim (h₁ a) (h₂ a)) =
l₁.pmap f h₁ ++ l₂.pmap f h₂ :=
pmap_append f l₁ l₂ _
@[simp] theorem attach_append (xs ys : Array α) :
(xs ++ ys).attach = xs.attach.map (fun x, h => x, mem_append_left ys h) ++
ys.attach.map fun x, h => x, mem_append_right xs h := by
cases xs
cases ys
rw [attach_congr (List.append_toArray _ _)]
simp [List.attach_append, Function.comp_def]
@[simp] theorem attachWith_append {P : α Prop} {xs ys : Array α}
{H : (a : α), a xs ++ ys P a} :
(xs ++ ys).attachWith P H = xs.attachWith P (fun a h => H a (mem_append_left ys h)) ++
ys.attachWith P (fun a h => H a (mem_append_right xs h)) := by
simp [attachWith, attach_append, map_pmap, pmap_append]
@[simp] theorem pmap_reverse {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs.reverse P a) :
xs.reverse.pmap f H = (xs.pmap f (fun a h => H a (by simpa using h))).reverse := by
induction xs <;> simp_all
theorem reverse_pmap {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs P a) :
(xs.pmap f H).reverse = xs.reverse.pmap f (fun a h => H a (by simpa using h)) := by
rw [pmap_reverse]
@[simp] theorem attachWith_reverse {P : α Prop} {xs : Array α}
{H : (a : α), a xs.reverse P a} :
xs.reverse.attachWith P H =
(xs.attachWith P (fun a h => H a (by simpa using h))).reverse := by
cases xs
simp
theorem reverse_attachWith {P : α Prop} {xs : Array α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).reverse = (xs.reverse.attachWith P (fun a h => H a (by simpa using h))) := by
cases xs
simp
@[simp] theorem attach_reverse (xs : Array α) :
xs.reverse.attach = xs.attach.reverse.map fun x, h => x, by simpa using h := by
cases xs
rw [attach_congr (List.reverse_toArray _)]
simp
theorem reverse_attach (xs : Array α) :
xs.attach.reverse = xs.reverse.attach.map fun x, h => x, by simpa using h := by
cases xs
simp
@[simp] theorem back?_pmap {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs P a) :
(xs.pmap f H).back? = xs.attach.back?.map fun a, m => f a (H a m) := by
cases xs
simp
@[simp] theorem back?_attachWith {P : α Prop} {xs : Array α}
{H : (a : α), a xs P a} :
(xs.attachWith P H).back? = xs.back?.pbind (fun a h => some a, H _ (mem_of_back?_eq_some h)) := by
cases xs
simp
@[simp]
theorem back?_attach {xs : Array α} :
xs.attach.back? = xs.back?.pbind fun a h => some a, mem_of_back?_eq_some h := by
cases xs
simp
/-! ## unattach
`Array.unattach` is the (one-sided) inverse of `Array.attach`. It is a synonym for `Array.map Subtype.val`.
We use it by providing a simp lemma `l.attach.unattach = l`, and simp lemmas which recognize higher order
functions applied to `l : Array { x // p x }` which only depend on the value, not the predicate, and rewrite these
in terms of a simpler function applied to `l.unattach`.
Further, we provide simp lemmas that push `unattach` inwards.
-/
/--
A synonym for `l.map (·.val)`. Mostly this should not be needed by users.
It is introduced as in intermediate step by lemmas such as `map_subtype`,
and is ideally subsequently simplified away by `unattach_attach`.
If not, usually the right approach is `simp [Array.unattach, -Array.map_subtype]` to unfold.
-/
def unattach {α : Type _} {p : α Prop} (l : Array { x // p x }) := l.map (·.val)
@[simp] theorem unattach_nil {p : α Prop} : (#[] : Array { x // p x }).unattach = #[] := rfl
@[simp] theorem unattach_push {p : α Prop} {a : { x // p x }} {l : Array { x // p x }} :
(l.push a).unattach = l.unattach.push a.1 := by
simp only [unattach, Array.map_push]
@[simp] theorem size_unattach {p : α Prop} {l : Array { x // p x }} :
l.unattach.size = l.size := by
unfold unattach
simp
@[simp] theorem _root_.List.unattach_toArray {p : α Prop} {l : List { x // p x }} :
l.toArray.unattach = l.unattach.toArray := by
simp only [unattach, List.map_toArray, List.unattach]
@[simp] theorem toList_unattach {p : α Prop} {l : Array { x // p x }} :
l.unattach.toList = l.toList.unattach := by
simp only [unattach, toList_map, List.unattach]
@[simp] theorem unattach_attach {l : Array α} : l.attach.unattach = l := by
cases l
simp only [List.attach_toArray, List.unattach_toArray, List.unattach_attachWith]
@[simp] theorem unattach_attachWith {p : α Prop} {l : Array α}
{H : a l, p a} :
(l.attachWith p H).unattach = l := by
cases l
simp
@[simp] theorem getElem?_unattach {p : α Prop} {l : Array { x // p x }} (i : Nat) :
l.unattach[i]? = l[i]?.map Subtype.val := by
simp [unattach]
@[simp] theorem getElem_unattach
{p : α Prop} {l : Array { x // p x }} (i : Nat) (h : i < l.unattach.size) :
l.unattach[i] = (l[i]'(by simpa using h)).1 := by
simp [unattach]
/-! ### Recognizing higher order functions using a function that only depends on the value. -/
/--
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
and simplifies these to the function directly taking the value.
-/
theorem foldl_subtype {p : α Prop} {l : Array { x // p x }}
{f : β { x // p x } β} {g : β α β} {x : β}
{hf : b x h, f b x, h = g b x} :
l.foldl f x = l.unattach.foldl g x := by
cases l
simp only [List.foldl_toArray', List.unattach_toArray]
rw [List.foldl_subtype] -- Why can't simp do this?
simp [hf]
/-- Variant of `foldl_subtype` with side condition to check `stop = l.size`. -/
@[simp] theorem foldl_subtype' {p : α Prop} {l : Array { x // p x }}
{f : β { x // p x } β} {g : β α β} {x : β}
{hf : b x h, f b x, h = g b x} (h : stop = l.size) :
l.foldl f x 0 stop = l.unattach.foldl g x := by
subst h
rwa [foldl_subtype]
/--
This lemma identifies folds over arrays of subtypes, where the function only depends on the value, not the proposition,
and simplifies these to the function directly taking the value.
-/
theorem foldr_subtype {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } β β} {g : α β β} {x : β}
{hf : x h b, f x, h b = g x b} :
l.foldr f x = l.unattach.foldr g x := by
cases l
simp only [List.foldr_toArray', List.unattach_toArray]
rw [List.foldr_subtype]
simp [hf]
/-- Variant of `foldr_subtype` with side condition to check `stop = l.size`. -/
@[simp] theorem foldr_subtype' {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } β β} {g : α β β} {x : β}
{hf : x h b, f x, h b = g x b} (h : start = l.size) :
l.foldr f x start 0 = l.unattach.foldr g x := by
subst h
rwa [foldr_subtype]
/--
This lemma identifies maps over arrays of subtypes, where the function only depends on the value, not the proposition,
and simplifies these to the function directly taking the value.
-/
@[simp] theorem map_subtype {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } β} {g : α β} {hf : x h, f x, h = g x} :
l.map f = l.unattach.map g := by
cases l
simp only [List.map_toArray, List.unattach_toArray]
rw [List.map_subtype]
simp [hf]
@[simp] theorem filterMap_subtype {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } Option β} {g : α Option β} {hf : x h, f x, h = g x} :
l.filterMap f = l.unattach.filterMap g := by
cases l
simp only [size_toArray, List.filterMap_toArray', List.unattach_toArray, List.length_unattach,
mk.injEq]
rw [List.filterMap_subtype]
simp [hf]
@[simp] theorem unattach_filter {p : α Prop} {l : Array { x // p x }}
{f : { x // p x } Bool} {g : α Bool} {hf : x h, f x, h = g x} :
(l.filter f).unattach = l.unattach.filter g := by
cases l
simp [hf]
/-! ### Simp lemmas pushing `unattach` inwards. -/
@[simp] theorem unattach_reverse {p : α Prop} {l : Array { x // p x }} :
l.reverse.unattach = l.unattach.reverse := by
cases l
simp
@[simp] theorem unattach_append {p : α Prop} {l₁ l₂ : Array { x // p x }} :
(l₁ ++ l₂).unattach = l₁.unattach ++ l₂.unattach := by
cases l₁
cases l₂
simp
end Array

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@ import Init.Data.Nat.Linear
import Init.NotationExtra import Init.NotationExtra
theorem Array.of_push_eq_push {as bs : Array α} (h : as.push a = bs.push b) : as = bs a = b := by theorem Array.of_push_eq_push {as bs : Array α} (h : as.push a = bs.push b) : as = bs a = b := by
simp only [push, mk.injEq] at h simp [push] at h
have h₁, h₂ := List.of_concat_eq_concat h have h₁, h₂ := List.of_concat_eq_concat h
cases as; cases bs cases as; cases bs
simp_all simp_all
@@ -34,11 +34,11 @@ private theorem List.of_toArrayAux_eq_toArrayAux {as bs : List α} {cs ds : Arra
@[simp] theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by @[simp] theorem List.toArray_eq_toArray_eq (as bs : List α) : (as.toArray = bs.toArray) = (as = bs) := by
apply propext; apply Iff.intro apply propext; apply Iff.intro
· intro h; simpa [toArray] using h · intro h; simp [toArray] at h; have := of_toArrayAux_eq_toArrayAux h rfl; exact this.1
· intro h; rw [h] · intro h; rw [h]
def Array.mapM' [Monad m] (f : α m β) (as : Array α) : m { bs : Array β // bs.size = as.size } := def Array.mapM' [Monad m] (f : α m β) (as : Array α) : m { bs : Array β // bs.size = as.size } :=
go 0 mkEmpty as.size, rfl (by simp) go 0 mkEmpty as.size, rfl (by simp_arith)
where where
go (i : Nat) (acc : { bs : Array β // bs.size = i }) (hle : i as.size) : m { bs : Array β // bs.size = as.size } := do go (i : Nat) (acc : { bs : Array β // bs.size = i }) (hle : i as.size) : m { bs : Array β // bs.size = as.size } := do
if h : i = as.size then if h : i = as.size then
@@ -60,7 +60,7 @@ where
if ptrEq a b then if ptrEq a b then
go (i+1) as go (i+1) as
else else
go (i+1) (as.set i b h) go (i+1) (as.set i, h b)
else else
return as return as

View File

@@ -5,64 +5,59 @@ Authors: Leonardo de Moura
-/ -/
prelude prelude
import Init.Data.Array.Basic import Init.Data.Array.Basic
import Init.Omega
universe u v universe u v
namespace Array -- TODO: CLEANUP
@[specialize] def binSearchAux {α : Type u} {β : Type v} (lt : α α Bool) (found : Option α β) (as : Array α) (k : α) : namespace Array
(lo : Fin (as.size + 1)) (hi : Fin as.size) (lo.1 hi.1) β -- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
| lo, hi, h => -- TODO: remove `partial` using well-founded recursion
let m := (lo.1 + hi.1)/2
let a := as[m] @[specialize] partial def binSearchAux {α : Type u} {β : Type v} [Inhabited β] (lt : α α Bool) (found : Option α β) (as : Array α) (k : α) : Nat Nat β
if lt a k then | lo, hi =>
if h' : m + 1 hi.1 then if lo <= hi then
binSearchAux lt found as k m+1, by omega hi h' let _ := Inhabited.mk k
else found none let m := (lo + hi)/2
else if lt k a then let a := as.get! m
if h' : m = 0 m - 1 < lo.1 then found none if lt a k then binSearchAux lt found as k (m+1) hi
else binSearchAux lt found as k lo m-1, by omega (by simp; omega) else if lt k a then
else found (some a) if m == 0 then found none
termination_by lo hi => hi.1 - lo.1 else binSearchAux lt found as k lo (m-1)
else found (some a)
else found none
@[inline] def binSearch {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Option α := @[inline] def binSearch {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Option α :=
if h : lo < as.size then if lo < as.size then
let hi := if hi < as.size then hi else as.size - 1 let hi := if hi < as.size then hi else as.size - 1
if w : lo hi then binSearchAux lt id as k lo hi
binSearchAux lt id as k lo, by omega hi, by simp [hi]; split <;> omega (by simp [hi]; omega)
else
none
else else
none none
@[inline] def binSearchContains {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Bool := @[inline] def binSearchContains {α : Type} (as : Array α) (k : α) (lt : α α Bool) (lo := 0) (hi := as.size - 1) : Bool :=
if h : lo < as.size then if lo < as.size then
let hi := if hi < as.size then hi else as.size - 1 let hi := if hi < as.size then hi else as.size - 1
if w : lo hi then binSearchAux lt Option.isSome as k lo hi
binSearchAux lt Option.isSome as k lo, by omega hi, by simp [hi]; split <;> omega (by simp [hi]; omega)
else
false
else else
false false
@[specialize] private def binInsertAux {α : Type u} {m : Type u Type v} [Monad m] @[specialize] private partial def binInsertAux {α : Type u} {m : Type u Type v} [Monad m]
(lt : α α Bool) (lt : α α Bool)
(merge : α m α) (merge : α m α)
(add : Unit m α) (add : Unit m α)
(as : Array α) (as : Array α)
(k : α) : (lo : Fin as.size) (hi : Fin as.size) (lo.1 hi.1) (lt as[lo] k) m (Array α) (k : α) : Nat Nat m (Array α)
| lo, hi, h, w => | lo, hi =>
let mid := (lo.1 + hi.1)/2 let _ := Inhabited.mk k
let midVal := as[mid] -- as[lo] < k < as[hi]
if w₁ : lt midVal k then let mid := (lo + hi)/2
if h' : mid = lo then do let v add (); pure <| as.insertIdx (lo+1) v let midVal := as.get! mid
else binInsertAux lt merge add as k mid, by omega hi (by simp; omega) w₁ if lt midVal k then
else if w₂ : lt k midVal then if mid == lo then do let v add (); pure <| as.insertAt! (lo+1) v
have : mid lo := fun z => by simp [midVal, z] at w₁; simp_all else binInsertAux lt merge add as k mid hi
binInsertAux lt merge add as k lo mid, by omega (by simp; omega) w else if lt k midVal then
binInsertAux lt merge add as k lo mid
else do else do
as.modifyM mid <| fun v => merge v as.modifyM mid <| fun v => merge v
termination_by lo hi => hi.1 - lo.1
@[specialize] def binInsertM {α : Type u} {m : Type u Type v} [Monad m] @[specialize] def binInsertM {α : Type u} {m : Type u Type v} [Monad m]
(lt : α α Bool) (lt : α α Bool)
@@ -70,12 +65,13 @@ termination_by lo hi => hi.1 - lo.1
(add : Unit m α) (add : Unit m α)
(as : Array α) (as : Array α)
(k : α) : m (Array α) := (k : α) : m (Array α) :=
if h : as.size = 0 then do let v add (); pure <| as.push v let _ := Inhabited.mk k
else if lt k as[0] then do let v add (); pure <| as.insertIdx 0 v if as.isEmpty then do let v add (); pure <| as.push v
else if h' : !lt as[0] k then as.modifyM 0 <| merge else if lt k (as.get! 0) then do let v add (); pure <| as.insertAt! 0 v
else if lt as[as.size - 1] k then do let v add (); pure <| as.push v else if !lt (as.get! 0) k then as.modifyM 0 <| merge
else if !lt k as[as.size - 1] then as.modifyM (as.size - 1) <| merge else if lt as.back k then do let v add (); pure <| as.push v
else binInsertAux lt merge add as k 0, by omega as.size - 1, by omega (by simp) (by simpa using h') else if !lt k as.back then as.modifyM (as.size - 1) <| merge
else binInsertAux lt merge add as k 0 (as.size - 1)
@[inline] def binInsert {α : Type u} (lt : α α Bool) (as : Array α) (k : α) : Array α := @[inline] def binInsert {α : Type u} (lt : α α Bool) (as : Array α) (k : α) : Array α :=
Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k Id.run <| binInsertM lt (fun _ => k) (fun _ => k) as k

View File

@@ -1,155 +0,0 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro
-/
prelude
import Init.Data.List.TakeDrop
/-!
## Bootstrapping theorems about arrays
This file contains some theorems about `Array` and `List` needed for `Init.Data.List.Impl`.
-/
namespace Array
theorem foldlM_toList.aux [Monad m]
(f : β α m β) (arr : Array α) (i j) (H : arr.size i + j) (b) :
foldlM.loop f arr arr.size (Nat.le_refl _) i j b = (arr.toList.drop j).foldlM f b := by
unfold foldlM.loop
split; split
· cases Nat.not_le_of_gt _ (Nat.zero_add _ H)
· rename_i i; rw [Nat.succ_add] at H
simp [foldlM_toList.aux f arr i (j+1) H]
rw (occs := [2]) [ List.getElem_cons_drop_succ_eq_drop _]
rfl
· rw [List.drop_of_length_le (Nat.ge_of_not_lt _)]; rfl
@[simp] theorem foldlM_toList [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
arr.toList.foldlM f init = arr.foldlM f init := by
simp [foldlM, foldlM_toList.aux]
@[simp] theorem foldl_toList (f : β α β) (init : β) (arr : Array α) :
arr.toList.foldl f init = arr.foldl f init :=
List.foldl_eq_foldlM .. foldlM_toList ..
theorem foldrM_eq_reverse_foldlM_toList.aux [Monad m]
(f : α β m β) (arr : Array α) (init : β) (i h) :
(arr.toList.take i).reverse.foldlM (fun x y => f y x) init = foldrM.fold f arr 0 i h init := by
unfold foldrM.fold
match i with
| 0 => simp [List.foldlM, List.take]
| i+1 => rw [ List.take_concat_get _ _ h]; simp [ (aux f arr · i)]
theorem foldrM_eq_reverse_foldlM_toList [Monad m] (f : α β m β) (init : β) (arr : Array α) :
arr.foldrM f init = arr.toList.reverse.foldlM (fun x y => f y x) init := by
have : arr = #[] 0 < arr.size :=
match arr with | [] => .inl rfl | a::l => .inr (Nat.zero_lt_succ _)
match arr, this with | _, .inl rfl => rfl | arr, .inr h => ?_
simp [foldrM, h, foldrM_eq_reverse_foldlM_toList.aux, List.take_length]
@[simp] theorem foldrM_toList [Monad m]
(f : α β m β) (init : β) (arr : Array α) :
arr.toList.foldrM f init = arr.foldrM f init := by
rw [foldrM_eq_reverse_foldlM_toList, List.foldlM_reverse]
@[simp] theorem foldr_toList (f : α β β) (init : β) (arr : Array α) :
arr.toList.foldr f init = arr.foldr f init :=
List.foldr_eq_foldrM .. foldrM_toList ..
@[simp] theorem push_toList (arr : Array α) (a : α) : (arr.push a).toList = arr.toList ++ [a] := by
simp [push, List.concat_eq_append]
@[simp] theorem toListAppend_eq (arr : Array α) (l) : arr.toListAppend l = arr.toList ++ l := by
simp [toListAppend, foldr_toList]
@[simp] theorem toListImpl_eq (arr : Array α) : arr.toListImpl = arr.toList := by
simp [toListImpl, foldr_toList]
@[simp] theorem pop_toList (arr : Array α) : arr.pop.toList = arr.toList.dropLast := rfl
@[simp] theorem append_eq_append (arr arr' : Array α) : arr.append arr' = arr ++ arr' := rfl
@[simp] theorem toList_append (arr arr' : Array α) :
(arr ++ arr').toList = arr.toList ++ arr'.toList := by
rw [ append_eq_append]; unfold Array.append
rw [ foldl_toList]
induction arr'.toList generalizing arr <;> simp [*]
@[simp] theorem toList_empty : (#[] : Array α).toList = [] := rfl
@[simp] theorem append_nil (as : Array α) : as ++ #[] = as := by
apply ext'; simp only [toList_append, toList_empty, List.append_nil]
@[simp] theorem nil_append (as : Array α) : #[] ++ as = as := by
apply ext'; simp only [toList_append, toList_empty, List.nil_append]
@[simp] theorem append_assoc (as bs cs : Array α) : as ++ bs ++ cs = as ++ (bs ++ cs) := by
apply ext'; simp only [toList_append, List.append_assoc]
@[simp] theorem appendList_eq_append
(arr : Array α) (l : List α) : arr.appendList l = arr ++ l := rfl
@[simp] theorem appendList_toList (arr : Array α) (l : List α) :
(arr ++ l).toList = arr.toList ++ l := by
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing arr <;> simp [*]
@[deprecated "Use the reverse direction of `foldrM_toList`." (since := "2024-11-13")]
theorem foldrM_eq_foldrM_toList [Monad m]
(f : α β m β) (init : β) (arr : Array α) :
arr.foldrM f init = arr.toList.foldrM f init := by
simp
@[deprecated "Use the reverse direction of `foldlM_toList`." (since := "2024-11-13")]
theorem foldlM_eq_foldlM_toList [Monad m]
(f : β α m β) (init : β) (arr : Array α) :
arr.foldlM f init = arr.toList.foldlM f init:= by
simp
@[deprecated "Use the reverse direction of `foldr_toList`." (since := "2024-11-13")]
theorem foldr_eq_foldr_toList
(f : α β β) (init : β) (arr : Array α) :
arr.foldr f init = arr.toList.foldr f init := by
simp
@[deprecated "Use the reverse direction of `foldl_toList`." (since := "2024-11-13")]
theorem foldl_eq_foldl_toList
(f : β α β) (init : β) (arr : Array α) :
arr.foldl f init = arr.toList.foldl f init:= by
simp
@[deprecated foldlM_toList (since := "2024-09-09")]
abbrev foldlM_eq_foldlM_data := @foldlM_toList
@[deprecated foldl_toList (since := "2024-09-09")]
abbrev foldl_eq_foldl_data := @foldl_toList
@[deprecated foldrM_eq_reverse_foldlM_toList (since := "2024-09-09")]
abbrev foldrM_eq_reverse_foldlM_data := @foldrM_eq_reverse_foldlM_toList
@[deprecated foldrM_toList (since := "2024-09-09")]
abbrev foldrM_eq_foldrM_data := @foldrM_toList
@[deprecated foldr_toList (since := "2024-09-09")]
abbrev foldr_eq_foldr_data := @foldr_toList
@[deprecated push_toList (since := "2024-09-09")]
abbrev push_data := @push_toList
@[deprecated toListImpl_eq (since := "2024-09-09")]
abbrev toList_eq := @toListImpl_eq
@[deprecated pop_toList (since := "2024-09-09")]
abbrev pop_data := @pop_toList
@[deprecated toList_append (since := "2024-09-09")]
abbrev append_data := @toList_append
@[deprecated appendList_toList (since := "2024-09-09")]
abbrev appendList_data := @appendList_toList
end Array

View File

@@ -5,81 +5,43 @@ Authors: Leonardo de Moura
-/ -/
prelude prelude
import Init.Data.Array.Basic import Init.Data.Array.Basic
import Init.Data.BEq
import Init.Data.List.Nat.BEq
import Init.ByCases import Init.ByCases
namespace Array namespace Array
theorem rel_of_isEqvAux theorem eq_of_isEqvAux [DecidableEq α] (a b : Array α) (hsz : a.size = b.size) (i : Nat) (hi : i a.size) (heqv : Array.isEqvAux a b hsz (fun x y => x = y) i) (j : Nat) (low : i j) (high : j < a.size) : a[j] = b[j]'(hsz high) := by
{r : α α Bool} {a b : Array α} (hsz : a.size = b.size) {i : Nat} (hi : i a.size) by_cases h : i < a.size
(heqv : Array.isEqvAux a b hsz r i hi) · unfold Array.isEqvAux at heqv
{j : Nat} (hj : j < i) : r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz hi))) := by simp [h] at heqv
induction i with have hind := eq_of_isEqvAux a b hsz (i+1) (Nat.succ_le_of_lt h) heqv.2
| zero => contradiction by_cases heq : i = j
| succ i ih => · subst heq; exact heqv.1
simp only [Array.isEqvAux, Bool.and_eq_true, decide_eq_true_eq] at heqv · exact hind j (Nat.succ_le_of_lt (Nat.lt_of_le_of_ne low heq)) high
by_cases hj' : j < i · have heq : i = a.size := Nat.le_antisymm hi (Nat.ge_of_not_lt h)
next => subst heq
exact ih _ heqv.right hj' exact absurd (Nat.lt_of_lt_of_le high low) (Nat.lt_irrefl j)
next => termination_by a.size - i
replace hj' : j = i := Nat.eq_of_le_of_lt_succ (Nat.not_lt.mp hj') hj decreasing_by decreasing_trivial_pre_omega
subst hj'
exact heqv.left
theorem isEqvAux_of_rel {r : α α Bool} {a b : Array α} (hsz : a.size = b.size) {i : Nat} (hi : i a.size)
(w : j, (hj : j < i) r (a[j]'(Nat.lt_of_lt_of_le hj hi)) (b[j]'(Nat.lt_of_lt_of_le hj (hsz hi)))) : Array.isEqvAux a b hsz r i hi := by
induction i with
| zero => simp [Array.isEqvAux]
| succ i ih =>
simp only [isEqvAux, Bool.and_eq_true]
exact w i (Nat.lt_add_one i), ih _ fun j hj => w j (Nat.lt_add_right 1 hj)
theorem rel_of_isEqv {r : α α Bool} {a b : Array α} : theorem eq_of_isEqv [DecidableEq α] (a b : Array α) : Array.isEqv a b (fun x y => x = y) a = b := by
Array.isEqv a b r h : a.size = b.size, (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h h')) := by simp [Array.isEqv]
simp only [isEqv] split
split <;> rename_i h case inr => intro; contradiction
· exact fun h' => h, fun i => rel_of_isEqvAux h (Nat.le_refl ..) h' case inl hsz =>
· intro; contradiction intro h
have aux := eq_of_isEqvAux a b hsz 0 (Nat.zero_le ..) h
exact ext a b hsz fun i h _ => aux i (Nat.zero_le ..) _
theorem isEqv_iff_rel (a b : Array α) (r) : theorem isEqvAux_self [DecidableEq α] (a : Array α) (i : Nat) : Array.isEqvAux a a rfl (fun x y => x = y) i = true := by
Array.isEqv a b r h : a.size = b.size, (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h h')) := unfold Array.isEqvAux
rel_of_isEqv, fun h, w => by split
simp only [isEqv, h, reduceDIte] case inl h => simp [h, isEqvAux_self a (i+1)]
exact isEqvAux_of_rel h (by simp [h]) w case inr h => simp [h]
termination_by a.size - i
decreasing_by decreasing_trivial_pre_omega
theorem isEqv_eq_decide (a b : Array α) (r) : theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (fun x y => x = y) = true := by
Array.isEqv a b r =
if h : a.size = b.size then decide ( (i : Nat) (h' : i < a.size), r (a[i]) (b[i]'(h h'))) else false := by
by_cases h : Array.isEqv a b r
· simp only [h, Bool.true_eq]
simp only [isEqv_iff_rel] at h
obtain h, w := h
simp [h, w]
· let h' := h
simp only [Bool.not_eq_true] at h
simp only [h, Bool.false_eq, dite_eq_right_iff, decide_eq_false_iff_not, Classical.not_forall,
Bool.not_eq_true]
simpa [isEqv_iff_rel] using h'
@[simp] theorem isEqv_toList [BEq α] (a b : Array α) : (a.toList.isEqv b.toList r) = (a.isEqv b r) := by
simp [isEqv_eq_decide, List.isEqv_eq_decide]
theorem eq_of_isEqv [DecidableEq α] (a b : Array α) (h : Array.isEqv a b (fun x y => x = y)) : a = b := by
have h, h' := rel_of_isEqv h
exact ext _ _ h (fun i lt _ => by simpa using h' i lt)
theorem isEqvAux_self (r : α α Bool) (hr : a, r a a) (a : Array α) (i : Nat) (h : i a.size) :
Array.isEqvAux a a rfl r i h = true := by
induction i with
| zero => simp [Array.isEqvAux]
| succ i ih =>
simp_all only [isEqvAux, Bool.and_self]
theorem isEqv_self_beq [BEq α] [ReflBEq α] (a : Array α) : Array.isEqv a a (· == ·) = true := by
simp [isEqv, isEqvAux_self]
theorem isEqv_self [DecidableEq α] (a : Array α) : Array.isEqv a a (· = ·) = true := by
simp [isEqv, isEqvAux_self] simp [isEqv, isEqvAux_self]
instance [DecidableEq α] : DecidableEq (Array α) := instance [DecidableEq α] : DecidableEq (Array α) :=
@@ -88,22 +50,4 @@ instance [DecidableEq α] : DecidableEq (Array α) :=
| true => isTrue (eq_of_isEqv a b h) | true => isTrue (eq_of_isEqv a b h)
| false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction | false => isFalse fun h' => by subst h'; rw [isEqv_self] at h; contradiction
theorem beq_eq_decide [BEq α] (a b : Array α) :
(a == b) = if h : a.size = b.size then
decide ( (i : Nat) (h' : i < a.size), a[i] == b[i]'(h h')) else false := by
simp [BEq.beq, isEqv_eq_decide]
@[simp] theorem beq_toList [BEq α] (a b : Array α) : (a.toList == b.toList) = (a == b) := by
simp [beq_eq_decide, List.beq_eq_decide]
end Array end Array
namespace List
@[simp] theorem isEqv_toArray [BEq α] (a b : List α) : (a.toArray.isEqv b.toArray r) = (a.isEqv b r) := by
simp [isEqv_eq_decide, Array.isEqv_eq_decide]
@[simp] theorem beq_toArray [BEq α] (a b : List α) : (a.toArray == b.toArray) = (a == b) := by
simp [beq_eq_decide, Array.beq_eq_decide]
end List

View File

@@ -1,14 +0,0 @@
/-
Copyright (c) 2024 François G. Dorais. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: François G. Dorais
-/
prelude
import Init.Data.List.FinRange
namespace Array
/-- `finRange n` is the array of all elements of `Fin n` in order. -/
protected def finRange (n : Nat) : Array (Fin n) := ofFn fun i => i
end Array

View File

@@ -1,281 +0,0 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Find
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
/-!
# Lemmas about `Array.findSome?`, `Array.find?`.
-/
namespace Array
open Nat
/-! ### findSome? -/
@[simp] theorem findSomeRev?_push_of_isSome (l : Array α) (h : (f a).isSome) : (l.push a).findSomeRev? f = f a := by
cases l; simp_all
@[simp] theorem findSomeRev?_push_of_isNone (l : Array α) (h : (f a).isNone) : (l.push a).findSomeRev? f = l.findSomeRev? f := by
cases l; simp_all
theorem exists_of_findSome?_eq_some {f : α Option β} {l : Array α} (w : l.findSome? f = some b) :
a, a l f a = b := by
cases l; simp_all [List.exists_of_findSome?_eq_some]
@[simp] theorem findSome?_eq_none_iff : findSome? p l = none x l, p x = none := by
cases l; simp
@[simp] theorem findSome?_isSome_iff {f : α Option β} {l : Array α} :
(l.findSome? f).isSome x, x l (f x).isSome := by
cases l; simp
theorem findSome?_eq_some_iff {f : α Option β} {l : Array α} {b : β} :
l.findSome? f = some b (l₁ : Array α) (a : α) (l₂ : Array α), l = l₁.push a ++ l₂ f a = some b x l₁, f x = none := by
cases l
simp only [List.findSome?_toArray, List.findSome?_eq_some_iff]
constructor
· rintro l₁, a, l₂, rfl, h₁, h₂
exact l₁.toArray, a, l₂.toArray, by simp_all
· rintro l₁, a, l₂, h₀, h₁, h₂
exact l₁.toList, a, l₂.toList, by simpa using congrArg toList h₀, h₁, by simpa
@[simp] theorem findSome?_guard (l : Array α) : findSome? (Option.guard fun x => p x) l = find? p l := by
cases l; simp
@[simp] theorem getElem?_zero_filterMap (f : α Option β) (l : Array α) : (l.filterMap f)[0]? = l.findSome? f := by
cases l; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filterMap (f : α Option β) (l : Array α) (h) :
(l.filterMap f)[0] = (l.findSome? f).get (by cases l; simpa [List.length_filterMap_eq_countP] using h) := by
cases l; simp [ List.head_eq_getElem, getElem?_zero_filterMap]
@[simp] theorem back?_filterMap (f : α Option β) (l : Array α) : (l.filterMap f).back? = l.findSomeRev? f := by
cases l; simp
@[simp] theorem back!_filterMap [Inhabited β] (f : α Option β) (l : Array α) :
(l.filterMap f).back! = (l.findSomeRev? f).getD default := by
cases l; simp
@[simp] theorem map_findSome? (f : α Option β) (g : β γ) (l : Array α) :
(l.findSome? f).map g = l.findSome? (Option.map g f) := by
cases l; simp
theorem findSome?_map (f : β γ) (l : Array β) : findSome? p (l.map f) = l.findSome? (p f) := by
cases l; simp [List.findSome?_map]
theorem findSome?_append {l₁ l₂ : Array α} : (l₁ ++ l₂).findSome? f = (l₁.findSome? f).or (l₂.findSome? f) := by
cases l₁; cases l₂; simp [List.findSome?_append]
theorem getElem?_zero_flatten (L : Array (Array α)) :
(flatten L)[0]? = L.findSome? fun l => l[0]? := by
cases L using array_array_induction
simp [ List.head?_eq_getElem?, List.head?_flatten, List.findSome?_map, Function.comp_def]
theorem getElem_zero_flatten.proof {L : Array (Array α)} (h : 0 < L.flatten.size) :
(L.findSome? fun l => l[0]?).isSome := by
cases L using array_array_induction
simp only [List.findSome?_toArray, List.findSome?_map, Function.comp_def, List.getElem?_toArray,
List.findSome?_isSome_iff, isSome_getElem?]
simp only [flatten_toArray_map_toArray, size_toArray, List.length_flatten,
Nat.sum_pos_iff_exists_pos, List.mem_map] at h
obtain _, xs, m, rfl, h := h
exact xs, m, by simpa using h
theorem getElem_zero_flatten {L : Array (Array α)} (h) :
(flatten L)[0] = (L.findSome? fun l => l[0]?).get (getElem_zero_flatten.proof h) := by
have t := getElem?_zero_flatten L
simp [getElem?_eq_getElem, h] at t
simp [ t]
theorem back?_flatten {L : Array (Array α)} :
(flatten L).back? = (L.findSomeRev? fun l => l.back?) := by
cases L using array_array_induction
simp [List.getLast?_flatten, List.map_reverse, List.findSome?_map, Function.comp_def]
theorem findSome?_mkArray : findSome? f (mkArray n a) = if n = 0 then none else f a := by
simp [mkArray_eq_toArray_replicate, List.findSome?_replicate]
@[simp] theorem findSome?_mkArray_of_pos (h : 0 < n) : findSome? f (mkArray n a) = f a := by
simp [findSome?_mkArray, Nat.ne_of_gt h]
-- Argument is unused, but used to decide whether `simp` should unfold.
@[simp] theorem findSome?_mkArray_of_isSome (_ : (f a).isSome) :
findSome? f (mkArray n a) = if n = 0 then none else f a := by
simp [findSome?_mkArray]
@[simp] theorem findSome?_mkArray_of_isNone (h : (f a).isNone) :
findSome? f (mkArray n a) = none := by
rw [Option.isNone_iff_eq_none] at h
simp [findSome?_mkArray, h]
/-! ### find? -/
@[simp] theorem find?_singleton (a : α) (p : α Bool) :
#[a].find? p = if p a then some a else none := by
simp [singleton_eq_toArray_singleton]
@[simp] theorem findRev?_push_of_pos (l : Array α) (h : p a) :
findRev? p (l.push a) = some a := by
cases l; simp [h]
@[simp] theorem findRev?_cons_of_neg (l : Array α) (h : ¬p a) :
findRev? p (l.push a) = findRev? p l := by
cases l; simp [h]
@[simp] theorem find?_eq_none : find? p l = none x l, ¬ p x := by
cases l; simp
theorem find?_eq_some_iff_append {xs : Array α} :
xs.find? p = some b p b (as bs : Array α), xs = as.push b ++ bs a as, !p a := by
rcases xs with xs
simp only [List.find?_toArray, List.find?_eq_some_iff_append, Bool.not_eq_eq_eq_not,
Bool.not_true, exists_and_right, and_congr_right_iff]
intro w
constructor
· rintro as, x, rfl, h
exact as.toArray, x.toArray, by simp , by simpa using h
· rintro as, x, h', h
exact as.toList, x.toList, by simpa using congrArg Array.toList h',
by simpa using h
@[simp]
theorem find?_push_eq_some {xs : Array α} :
(xs.push a).find? p = some b xs.find? p = some b (xs.find? p = none (p a a = b)) := by
cases xs; simp
@[simp] theorem find?_isSome {xs : Array α} {p : α Bool} : (xs.find? p).isSome x, x xs p x := by
cases xs; simp
theorem find?_some {xs : Array α} (h : find? p xs = some a) : p a := by
cases xs
simp at h
exact List.find?_some h
theorem mem_of_find?_eq_some {xs : Array α} (h : find? p xs = some a) : a xs := by
cases xs
simp at h
simpa using List.mem_of_find?_eq_some h
theorem get_find?_mem {xs : Array α} (h) : (xs.find? p).get h xs := by
cases xs
simp [List.get_find?_mem]
@[simp] theorem find?_filter {xs : Array α} (p q : α Bool) :
(xs.filter p).find? q = xs.find? (fun a => p a q a) := by
cases xs; simp
@[simp] theorem getElem?_zero_filter (p : α Bool) (l : Array α) :
(l.filter p)[0]? = l.find? p := by
cases l; simp [ List.head?_eq_getElem?]
@[simp] theorem getElem_zero_filter (p : α Bool) (l : Array α) (h) :
(l.filter p)[0] =
(l.find? p).get (by cases l; simpa [ List.countP_eq_length_filter] using h) := by
cases l
simp [List.getElem_zero_eq_head]
@[simp] theorem back?_filter (p : α Bool) (l : Array α) : (l.filter p).back? = l.findRev? p := by
cases l; simp
@[simp] theorem back!_filter [Inhabited α] (p : α Bool) (l : Array α) :
(l.filter p).back! = (l.findRev? p).get! := by
cases l; simp [Option.get!_eq_getD]
@[simp] theorem find?_filterMap (xs : Array α) (f : α Option β) (p : β Bool) :
(xs.filterMap f).find? p = (xs.find? (fun a => (f a).any p)).bind f := by
cases xs; simp
@[simp] theorem find?_map (f : β α) (xs : Array β) :
find? p (xs.map f) = (xs.find? (p f)).map f := by
cases xs; simp
@[simp] theorem find?_append {l₁ l₂ : Array α} :
(l₁ ++ l₂).find? p = (l₁.find? p).or (l₂.find? p) := by
cases l₁
cases l₂
simp
@[simp] theorem find?_flatten (xs : Array (Array α)) (p : α Bool) :
xs.flatten.find? p = xs.findSome? (·.find? p) := by
cases xs using array_array_induction
simp [List.findSome?_map, Function.comp_def]
theorem find?_flatten_eq_none {xs : Array (Array α)} {p : α Bool} :
xs.flatten.find? p = none ys xs, x ys, !p x := by
simp
/--
If `find? p` returns `some a` from `xs.flatten`, then `p a` holds, and
some array in `xs` contains `a`, and no earlier element of that array satisfies `p`.
Moreover, no earlier array in `xs` has an element satisfying `p`.
-/
theorem find?_flatten_eq_some {xs : Array (Array α)} {p : α Bool} {a : α} :
xs.flatten.find? p = some a
p a (as : Array (Array α)) (ys zs : Array α) (bs : Array (Array α)),
xs = as.push (ys.push a ++ zs) ++ bs
( a as, x a, !p x) ( x ys, !p x) := by
cases xs using array_array_induction
simp only [flatten_toArray_map_toArray, List.find?_toArray, List.find?_flatten_eq_some]
simp only [Bool.not_eq_eq_eq_not, Bool.not_true, exists_and_right, and_congr_right_iff]
intro w
constructor
· rintro as, ys, zs, bs, rfl, h₁, h₂
exact as.toArray.map List.toArray, ys.toArray,
zs.toArray, bs.toArray.map List.toArray, by simp, by simpa using h₁, by simpa using h₂
· rintro as, ys, zs, bs, h, h₁, h₂
replace h := congrArg (·.map Array.toList) (congrArg Array.toList h)
simp [Function.comp_def] at h
exact as.toList.map Array.toList, ys.toList,
zs.toList, bs.toList.map Array.toList, by simpa using h,
by simpa using h₁, by simpa using h₂
@[simp] theorem find?_flatMap (xs : Array α) (f : α Array β) (p : β Bool) :
(xs.flatMap f).find? p = xs.findSome? (fun x => (f x).find? p) := by
cases xs
simp [List.find?_flatMap, Array.flatMap_toArray]
theorem find?_flatMap_eq_none {xs : Array α} {f : α Array β} {p : β Bool} :
(xs.flatMap f).find? p = none x xs, y f x, !p y := by
simp
theorem find?_mkArray :
find? p (mkArray n a) = if n = 0 then none else if p a then some a else none := by
simp [mkArray_eq_toArray_replicate, List.find?_replicate]
@[simp] theorem find?_mkArray_of_length_pos (h : 0 < n) :
find? p (mkArray n a) = if p a then some a else none := by
simp [find?_mkArray, Nat.ne_of_gt h]
@[simp] theorem find?_mkArray_of_pos (h : p a) :
find? p (mkArray n a) = if n = 0 then none else some a := by
simp [find?_mkArray, h]
@[simp] theorem find?_mkArray_of_neg (h : ¬ p a) : find? p (mkArray n a) = none := by
simp [find?_mkArray, h]
-- This isn't a `@[simp]` lemma since there is already a lemma for `l.find? p = none` for any `l`.
theorem find?_mkArray_eq_none {n : Nat} {a : α} {p : α Bool} :
(mkArray n a).find? p = none n = 0 !p a := by
simp [mkArray_eq_toArray_replicate, List.find?_replicate_eq_none, Classical.or_iff_not_imp_left]
@[simp] theorem find?_mkArray_eq_some {n : Nat} {a b : α} {p : α Bool} :
(mkArray n a).find? p = some b n 0 p a a = b := by
simp [mkArray_eq_toArray_replicate]
@[simp] theorem get_find?_mkArray (n : Nat) (a : α) (p : α Bool) (h) :
((mkArray n a).find? p).get h = a := by
simp [mkArray_eq_toArray_replicate]
theorem find?_pmap {P : α Prop} (f : (a : α) P a β) (xs : Array α)
(H : (a : α), a xs P a) (p : β Bool) :
(xs.pmap f H).find? p = (xs.attach.find? (fun a, m => p (f a (H a m)))).map fun a, m => f a (H a m) := by
simp only [pmap_eq_map_attach, find?_map]
rfl
end Array

View File

@@ -1,46 +0,0 @@
/-
Copyright (c) 2018 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Init.Data.Array.Basic
namespace Array
/-! ### getLit -/
-- auxiliary declaration used in the equation compiler when pattern matching array literals.
abbrev getLit {α : Type u} {n : Nat} (a : Array α) (i : Nat) (h₁ : a.size = n) (h₂ : i < n) : α :=
have := h₁.symm h₂
a[i]
theorem extLit {n : Nat}
(a b : Array α)
(hsz₁ : a.size = n) (hsz₂ : b.size = n)
(h : (i : Nat) (hi : i < n) a.getLit i hsz₁ hi = b.getLit i hsz₂ hi) : a = b :=
Array.ext a b (hsz₁.trans hsz₂.symm) fun i hi₁ _ => h i (hsz₁ hi₁)
def toListLitAux (a : Array α) (n : Nat) (hsz : a.size = n) : (i : Nat), i a.size List α List α
| 0, _, acc => acc
| (i+1), hi, acc => toListLitAux a n hsz i (Nat.le_of_succ_le hi) (a.getLit i hsz (Nat.lt_of_lt_of_eq (Nat.lt_of_lt_of_le (Nat.lt_succ_self i) hi) hsz) :: acc)
def toArrayLit (a : Array α) (n : Nat) (hsz : a.size = n) : Array α :=
List.toArray <| toListLitAux a n hsz n (hsz Nat.le_refl _) []
theorem toArrayLit_eq (as : Array α) (n : Nat) (hsz : as.size = n) : as = toArrayLit as n hsz := by
apply ext'
simp [toArrayLit, toList_toArray]
have hle : n as.size := hsz Nat.le_refl _
have hge : as.size n := hsz Nat.le_refl _
have := go n hle
rw [List.drop_eq_nil_of_le hge] at this
rw [this]
where
getLit_eq (as : Array α) (i : Nat) (h₁ : as.size = n) (h₂ : i < n) : as.getLit i h₁ h₂ = getElem as.toList i ((id (α := as.toList.length = n) h₁) h₂) :=
rfl
go (i : Nat) (hi : i as.size) : toListLitAux as n hsz i hi (as.toList.drop i) = as.toList := by
induction i <;> simp only [List.drop, toListLitAux, getLit_eq, List.getElem_cons_drop_succ_eq_drop, *]
end Array

View File

@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
prelude prelude
import Init.Data.Array.Basic import Init.Data.Array.Basic
@[inline] def Array.insertionSort (a : Array α) (lt : α α Bool := by exact (· < ·)) : Array α := @[inline] def Array.insertionSort (a : Array α) (lt : α α Bool) : Array α :=
traverse a 0 a.size traverse a 0 a.size
where where
@[specialize] traverse (a : Array α) (i : Nat) (fuel : Nat) : Array α := @[specialize] traverse (a : Array α) (i : Nat) (fuel : Nat) : Array α :=
@@ -23,6 +23,6 @@ where
| j'+1 => | j'+1 =>
have h' : j' < a.size := by subst j; exact Nat.lt_trans (Nat.lt_succ_self _) h have h' : j' < a.size := by subst j; exact Nat.lt_trans (Nat.lt_succ_self _) h
if lt a[j] a[j'] then if lt a[j] a[j'] then
swapLoop (a.swap j j') j' (by rw [size_swap]; assumption; done) swapLoop (a.swap j, h j', h') j' (by rw [size_swap]; assumption; done)
else else
a a

File diff suppressed because it is too large Load Diff

View File

@@ -1,112 +0,0 @@
/-
Copyright (c) 2022 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Kim Morrison
-/
prelude
import Init.Data.Array.Lemmas
import Init.Data.List.MapIdx
namespace Array
/-! ### mapFinIdx -/
-- This could also be proved from `SatisfiesM_mapIdxM` in Batteries.
theorem mapFinIdx_induction (as : Array α) (f : Fin as.size α β)
(motive : Nat Prop) (h0 : motive 0)
(p : Fin as.size β Prop)
(hs : i, motive i.1 p i (f i as[i]) motive (i + 1)) :
motive as.size eq : (Array.mapFinIdx as f).size = as.size,
i h, p i, h ((Array.mapFinIdx as f)[i]) := by
let rec go {bs i j h} (h₁ : j = bs.size) (h₂ : i h h', p i, h bs[i]) (hm : motive j) :
let arr : Array β := Array.mapFinIdxM.map (m := Id) as f i j h bs
motive as.size eq : arr.size = as.size, i h, p i, h arr[i] := by
induction i generalizing j bs with simp [mapFinIdxM.map]
| zero =>
have := (Nat.zero_add _).symm.trans h
exact this hm, h₁ this, fun _ _ => h₂ ..
| succ i ih =>
apply @ih (bs.push (f j, by omega as[j])) (j + 1) (by omega) (by simp; omega)
· intro i i_lt h'
rw [getElem_push]
split
· apply h₂
· simp only [size_push] at h'
obtain rfl : i = j := by omega
apply (hs i, by omega hm).1
· exact (hs j, by omega hm).2
simp [mapFinIdx, mapFinIdxM]; exact go rfl nofun h0
theorem mapFinIdx_spec (as : Array α) (f : Fin as.size α β)
(p : Fin as.size β Prop) (hs : i, p i (f i as[i])) :
eq : (Array.mapFinIdx as f).size = as.size,
i h, p i, h ((Array.mapFinIdx as f)[i]) :=
(mapFinIdx_induction _ _ (fun _ => True) trivial p fun _ _ => hs .., trivial).2
@[simp] theorem size_mapFinIdx (a : Array α) (f : Fin a.size α β) : (a.mapFinIdx f).size = a.size :=
(mapFinIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
@[simp] theorem size_zipWithIndex (as : Array α) : as.zipWithIndex.size = as.size :=
Array.size_mapFinIdx _ _
@[simp] theorem getElem_mapFinIdx (a : Array α) (f : Fin a.size α β) (i : Nat)
(h : i < (mapFinIdx a f).size) :
(a.mapFinIdx f)[i] = f i, by simp_all (a[i]'(by simp_all)) :=
(mapFinIdx_spec _ _ (fun i b => b = f i a[i]) fun _ => rfl).2 i _
@[simp] theorem getElem?_mapFinIdx (a : Array α) (f : Fin a.size α β) (i : Nat) :
(a.mapFinIdx f)[i]? =
a[i]?.pbind fun b h => f i, (getElem?_eq_some_iff.1 h).1 b := by
simp only [getElem?_def, size_mapFinIdx, getElem_mapFinIdx]
split <;> simp_all
@[simp] theorem toList_mapFinIdx (a : Array α) (f : Fin a.size α β) :
(a.mapFinIdx f).toList = a.toList.mapFinIdx (fun i a => f i, by simp a) := by
apply List.ext_getElem <;> simp
/-! ### mapIdx -/
theorem mapIdx_induction (f : Nat α β) (as : Array α)
(motive : Nat Prop) (h0 : motive 0)
(p : Fin as.size β Prop)
(hs : i, motive i.1 p i (f i as[i]) motive (i + 1)) :
motive as.size eq : (as.mapIdx f).size = as.size,
i h, p i, h ((as.mapIdx f)[i]) :=
mapFinIdx_induction as (fun i a => f i a) motive h0 p hs
theorem mapIdx_spec (f : Nat α β) (as : Array α)
(p : Fin as.size β Prop) (hs : i, p i (f i as[i])) :
eq : (as.mapIdx f).size = as.size,
i h, p i, h ((as.mapIdx f)[i]) :=
(mapIdx_induction _ _ (fun _ => True) trivial p fun _ _ => hs .., trivial).2
@[simp] theorem size_mapIdx (f : Nat α β) (as : Array α) : (as.mapIdx f).size = as.size :=
(mapIdx_spec (p := fun _ _ => True) (hs := fun _ => trivial)).1
@[simp] theorem getElem_mapIdx (f : Nat α β) (as : Array α) (i : Nat)
(h : i < (as.mapIdx f).size) :
(as.mapIdx f)[i] = f i (as[i]'(by simp_all)) :=
(mapIdx_spec _ _ (fun i b => b = f i as[i]) fun _ => rfl).2 i (by simp_all)
@[simp] theorem getElem?_mapIdx (f : Nat α β) (as : Array α) (i : Nat) :
(as.mapIdx f)[i]? =
as[i]?.map (f i) := by
simp [getElem?_def, size_mapIdx, getElem_mapIdx]
@[simp] theorem toList_mapIdx (f : Nat α β) (as : Array α) :
(as.mapIdx f).toList = as.toList.mapIdx (fun i a => f i a) := by
apply List.ext_getElem <;> simp
end Array
namespace List
@[simp] theorem mapFinIdx_toArray (l : List α) (f : Fin l.length α β) :
l.toArray.mapFinIdx f = (l.mapFinIdx f).toArray := by
ext <;> simp
@[simp] theorem mapIdx_toArray (f : Nat α β) (l : List α) :
l.toArray.mapIdx f = (l.mapIdx f).toArray := by
ext <;> simp
end List

View File

@@ -10,16 +10,25 @@ import Init.Data.List.BasicAux
namespace Array namespace Array
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
-- NB: This is defined as a structure rather than a plain def so that a lemma
-- like `sizeOf_lt_of_mem` will not apply with no actual arrays around.
structure Mem (a : α) (as : Array α) : Prop where
val : a as.data
instance : Membership α (Array α) where
mem a as := Mem a as
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
cases as with | _ as => cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith) exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)
theorem sizeOf_get [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) : sizeOf (as.get i h) < sizeOf as := by @[simp] theorem sizeOf_get [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
cases as with | _ as => cases as with | _ as =>
simpa using Nat.lt_trans (List.sizeOf_get _ i, h) (by simp_arith) exact Nat.lt_trans (List.sizeOf_get ..) (by simp_arith)
@[simp] theorem sizeOf_getElem [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) : @[simp] theorem sizeOf_getElem [SizeOf α] (as : Array α) (i : Nat) (h : i < as.size) :
sizeOf (as[i]'h) < sizeOf as := sizeOf_get _ _ h sizeOf (as[i]'h) < sizeOf as := sizeOf_get _ _
/-- This tactic, added to the `decreasing_trivial` toolbox, proves that /-- This tactic, added to the `decreasing_trivial` toolbox, proves that
`sizeOf arr[i] < sizeOf arr`, which is useful for well founded recursions `sizeOf arr[i] < sizeOf arr`, which is useful for well founded recursions
@@ -29,8 +38,8 @@ macro "array_get_dec" : tactic =>
-- subsumed by simp -- subsumed by simp
-- | with_reducible apply sizeOf_get -- | with_reducible apply sizeOf_get
-- | with_reducible apply sizeOf_getElem -- | with_reducible apply sizeOf_getElem
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_get ..)); simp_arith | (with_reducible apply Nat.lt_trans (sizeOf_get ..)); simp_arith
| (with_reducible apply Nat.lt_of_lt_of_le (sizeOf_getElem ..)); simp_arith | (with_reducible apply Nat.lt_trans (sizeOf_getElem ..)); simp_arith
) )
macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec) macro_rules | `(tactic| decreasing_trivial) => `(tactic| array_get_dec)
@@ -43,7 +52,7 @@ macro "array_mem_dec" : tactic =>
`(tactic| first `(tactic| first
| with_reducible apply Array.sizeOf_lt_of_mem; assumption; done | with_reducible apply Array.sizeOf_lt_of_mem; assumption; done
| with_reducible | with_reducible
apply Nat.lt_of_lt_of_le (Array.sizeOf_lt_of_mem ?h) apply Nat.lt_trans (Array.sizeOf_lt_of_mem ?h)
case' h => assumption case' h => assumption
simp_arith) simp_arith)

View File

@@ -1,159 +0,0 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.Array.Lemmas
import Init.Data.Array.Attach
import Init.Data.List.Monadic
/-!
# Lemmas about `Array.forIn'` and `Array.forIn`.
-/
namespace Array
open Nat
/-! ## Monadic operations -/
/-! ### mapM -/
theorem mapM_eq_foldlM_push [Monad m] [LawfulMonad m] (f : α m β) (l : Array α) :
mapM f l = l.foldlM (fun acc a => return (acc.push ( f a))) #[] := by
rcases l with l
simp only [List.mapM_toArray, bind_pure_comp, size_toArray, List.foldlM_toArray']
rw [List.mapM_eq_reverse_foldlM_cons]
simp only [bind_pure_comp, Functor.map_map]
suffices (k), (fun a => a.reverse.toArray) <$> List.foldlM (fun acc a => (fun a => a :: acc) <$> f a) k l =
List.foldlM (fun acc a => acc.push <$> f a) k.reverse.toArray l by
exact this []
intro k
induction l generalizing k with
| nil => simp
| cons a as ih =>
simp [ih, List.foldlM_cons]
/-! ### foldlM and foldrM -/
theorem foldlM_map [Monad m] (f : β₁ β₂) (g : α β₂ m α) (l : Array β₁) (init : α) :
(l.map f).foldlM g init = l.foldlM (fun x y => g x (f y)) init := by
cases l
rw [List.map_toArray] -- Why doesn't this fire via `simp`?
simp [List.foldlM_map]
theorem foldrM_map [Monad m] [LawfulMonad m] (f : β₁ β₂) (g : β₂ α m α) (l : Array β₁)
(init : α) : (l.map f).foldrM g init = l.foldrM (fun x y => g (f x) y) init := by
cases l
rw [List.map_toArray] -- Why doesn't this fire via `simp`?
simp [List.foldrM_map]
theorem foldlM_filterMap [Monad m] [LawfulMonad m] (f : α Option β) (g : γ β m γ) (l : Array α) (init : γ) :
(l.filterMap f).foldlM g init =
l.foldlM (fun x y => match f y with | some b => g x b | none => pure x) init := by
cases l
rw [List.filterMap_toArray] -- Why doesn't this fire via `simp`?
simp [List.foldlM_filterMap]
rfl
theorem foldrM_filterMap [Monad m] [LawfulMonad m] (f : α Option β) (g : β γ m γ) (l : Array α) (init : γ) :
(l.filterMap f).foldrM g init =
l.foldrM (fun x y => match f x with | some b => g b y | none => pure y) init := by
cases l
rw [List.filterMap_toArray] -- Why doesn't this fire via `simp`?
simp [List.foldrM_filterMap]
rfl
theorem foldlM_filter [Monad m] [LawfulMonad m] (p : α Bool) (g : β α m β) (l : Array α) (init : β) :
(l.filter p).foldlM g init =
l.foldlM (fun x y => if p y then g x y else pure x) init := by
cases l
rw [List.filter_toArray] -- Why doesn't this fire via `simp`?
simp [List.foldlM_filter]
theorem foldrM_filter [Monad m] [LawfulMonad m] (p : α Bool) (g : α β m β) (l : Array α) (init : β) :
(l.filter p).foldrM g init =
l.foldrM (fun x y => if p x then g x y else pure y) init := by
cases l
rw [List.filter_toArray] -- Why doesn't this fire via `simp`?
simp [List.foldrM_filter]
/-! ### forIn' -/
/--
We can express a for loop over an array as a fold,
in which whenever we reach `.done b` we keep that value through the rest of the fold.
-/
theorem forIn'_eq_foldlM [Monad m] [LawfulMonad m]
(l : Array α) (f : (a : α) a l β m (ForInStep β)) (init : β) :
forIn' l init f = ForInStep.value <$>
l.attach.foldlM (fun b a, m => match b with
| .yield b => f a m b
| .done b => pure (.done b)) (ForInStep.yield init) := by
cases l
rw [List.attach_toArray] -- Why doesn't this fire via `simp`?
simp only [List.forIn'_toArray, List.forIn'_eq_foldlM, List.attachWith_mem_toArray, size_toArray,
List.length_map, List.length_attach, List.foldlM_toArray', List.foldlM_map]
congr
/-- We can express a for loop over an array which always yields as a fold. -/
@[simp] theorem forIn'_yield_eq_foldlM [Monad m] [LawfulMonad m]
(l : Array α) (f : (a : α) a l β m γ) (g : (a : α) a l β γ β) (init : β) :
forIn' l init (fun a m b => (fun c => .yield (g a m b c)) <$> f a m b) =
l.attach.foldlM (fun b a, m => g a m b <$> f a m b) init := by
cases l
rw [List.attach_toArray] -- Why doesn't this fire via `simp`?
simp [List.foldlM_map]
theorem forIn'_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(l : Array α) (f : (a : α) a l β β) (init : β) :
forIn' l init (fun a m b => pure (.yield (f a m b))) =
pure (f := m) (l.attach.foldl (fun b a, h => f a h b) init) := by
cases l
simp [List.forIn'_pure_yield_eq_foldl, List.foldl_map]
@[simp] theorem forIn'_yield_eq_foldl
(l : Array α) (f : (a : α) a l β β) (init : β) :
forIn' (m := Id) l init (fun a m b => .yield (f a m b)) =
l.attach.foldl (fun b a, h => f a h b) init := by
cases l
simp [List.foldl_map]
/--
We can express a for loop over an array as a fold,
in which whenever we reach `.done b` we keep that value through the rest of the fold.
-/
theorem forIn_eq_foldlM [Monad m] [LawfulMonad m]
(f : α β m (ForInStep β)) (init : β) (l : Array α) :
forIn l init f = ForInStep.value <$>
l.foldlM (fun b a => match b with
| .yield b => f a b
| .done b => pure (.done b)) (ForInStep.yield init) := by
cases l
simp only [List.forIn_toArray, List.forIn_eq_foldlM, size_toArray, List.foldlM_toArray']
congr
/-- We can express a for loop over an array which always yields as a fold. -/
@[simp] theorem forIn_yield_eq_foldlM [Monad m] [LawfulMonad m]
(l : Array α) (f : α β m γ) (g : α β γ β) (init : β) :
forIn l init (fun a b => (fun c => .yield (g a b c)) <$> f a b) =
l.foldlM (fun b a => g a b <$> f a b) init := by
cases l
simp [List.foldlM_map]
theorem forIn_pure_yield_eq_foldl [Monad m] [LawfulMonad m]
(l : Array α) (f : α β β) (init : β) :
forIn l init (fun a b => pure (.yield (f a b))) =
pure (f := m) (l.foldl (fun b a => f a b) init) := by
cases l
simp [List.forIn_pure_yield_eq_foldl, List.foldl_map]
@[simp] theorem forIn_yield_eq_foldl
(l : Array α) (f : α β β) (init : β) :
forIn (m := Id) l init (fun a b => .yield (f a b)) =
l.foldl (fun b a => f a b) init := by
cases l
simp [List.foldl_map]
end Array

View File

@@ -1,65 +0,0 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kim Morrison
-/
prelude
import Init.Data.List.Nat.Perm
import Init.Data.Array.Lemmas
namespace Array
open List
/--
`Perm as bs` asserts that `as` and `bs` are permutations of each other.
This is a wrapper around `List.Perm`, and for now has much less API.
For more complicated verification, use `perm_iff_toList_perm` and the `List` API.
-/
def Perm (as bs : Array α) : Prop :=
as.toList ~ bs.toList
@[inherit_doc] scoped infixl:50 " ~ " => Perm
theorem perm_iff_toList_perm {as bs : Array α} : as ~ bs as.toList ~ bs.toList := Iff.rfl
@[simp] theorem perm_toArray (as bs : List α) : as.toArray ~ bs.toArray as ~ bs := by
simp [perm_iff_toList_perm]
@[simp, refl] protected theorem Perm.refl (l : Array α) : l ~ l := by
cases l
simp
protected theorem Perm.rfl {l : List α} : l ~ l := .refl _
theorem Perm.of_eq {l₁ l₂ : Array α} (h : l₁ = l₂) : l₁ ~ l₂ := h .rfl
protected theorem Perm.symm {l₁ l₂ : Array α} (h : l₁ ~ l₂) : l₂ ~ l₁ := by
cases l₁; cases l₂
simp only [perm_toArray] at h
simpa using h.symm
protected theorem Perm.trans {l₁ l₂ l₃ : Array α} (h₁ : l₁ ~ l₂) (h₂ : l₂ ~ l₃) : l₁ ~ l₃ := by
cases l₁; cases l₂; cases l₃
simp only [perm_toArray] at h₁ h₂
simpa using h₁.trans h₂
instance : Trans (Perm (α := α)) (Perm (α := α)) (Perm (α := α)) where
trans h₁ h₂ := Perm.trans h₁ h₂
theorem perm_comm {l₁ l₂ : Array α} : l₁ ~ l₂ l₂ ~ l₁ := Perm.symm, Perm.symm
theorem Perm.push (x y : α) {l₁ l₂ : Array α} (p : l₁ ~ l₂) :
(l₁.push x).push y ~ (l₂.push y).push x := by
cases l₁; cases l₂
simp only [perm_toArray] at p
simp only [push_toArray, List.append_assoc, singleton_append, perm_toArray]
exact p.append (Perm.swap' _ _ Perm.nil)
theorem swap_perm {as : Array α} {i j : Nat} (h₁ : i < as.size) (h₂ : j < as.size) :
as.swap i j ~ as := by
simp only [swap, perm_iff_toList_perm, toList_set]
apply set_set_perm
end Array

View File

@@ -4,52 +4,44 @@ Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura Authors: Leonardo de Moura
-/ -/
prelude prelude
import Init.Data.Vector.Basic import Init.Data.Array.Basic
import Init.Data.Ord
namespace Array namespace Array
-- TODO: remove the [Inhabited α] parameters as soon as we have the tactic framework for automating proof generation and using Array.fget
private def qpartition {n} (as : Vector α n) (lt : α α Bool) (lo hi : Nat) def qpartition (as : Array α) (lt : α α Bool) (lo hi : Nat) : Nat × Array α :=
(hlo : lo < n := by omega) (hhi : hi < n := by omega) : {n : Nat // lo n} × Vector α n := if h : as.size = 0 then (0, as) else have : Inhabited α := as[0]'(by revert h; cases as.size <;> simp) -- TODO: remove
let mid := (lo + hi) / 2 let mid := (lo + hi) / 2
let as := if lt as[mid] as[lo] then as.swap lo mid else as let as := if lt (as.get! mid) (as.get! lo) then as.swap! lo mid else as
let as := if lt as[hi] as[lo] then as.swap lo hi else as let as := if lt (as.get! hi) (as.get! lo) then as.swap! lo hi else as
let as := if lt as[mid] as[hi] then as.swap mid hi else as let as := if lt (as.get! mid) (as.get! hi) then as.swap! mid hi else as
let pivot := as[hi] let pivot := as.get! hi
let rec loop (as : Vector α n) (i j : Nat) let rec loop (as : Array α) (i j : Nat) :=
(ilo : lo i := by omega) (jh : j < n := by omega) (w : i j := by omega) :=
if h : j < hi then if h : j < hi then
if lt as[j] pivot then if lt (as.get! j) pivot then
loop (as.swap i j) (i+1) (j+1) let as := as.swap! i j
loop as (i+1) (j+1)
else else
loop as i (j+1) loop as i (j+1)
else else
(i, ilo, as.swap i hi) let as := as.swap! i hi
(i, as)
termination_by hi - j
decreasing_by all_goals simp_wf; decreasing_trivial_pre_omega
loop as lo lo loop as lo lo
@[inline] def qsort (as : Array α) (lt : α α Bool := by exact (· < ·)) @[inline] partial def qsort (as : Array α) (lt : α α Bool) (low := 0) (high := as.size - 1) : Array α :=
(low := 0) (high := as.size - 1) : Array α := let rec @[specialize] sort (as : Array α) (low high : Nat) :=
let rec @[specialize] sort {n} (as : Vector α n) (lo hi : Nat) if low < high then
(hlo : lo < n := by omega) (hhi : hi < n := by omega) := let p := qpartition as lt low high;
if h₁ : lo < hi then -- TODO: fix `partial` support in the equation compiler, it breaks if we use `let (mid, as) := partition as lt low high`
let mid, hmid, as := qpartition as lt lo hi let mid := p.1
if h₂ : mid hi then let as := p.2
as if mid >= high then as
else else
sort (sort as lo mid) (mid+1) hi let as := sort as low mid
sort as (mid+1) high
else as else as
if h : as.size = 0 then sort as low high
as
else
let low := min low (as.size - 1)
let high := min high (as.size - 1)
sort as, rfl low high |>.toArray
set_option linter.unusedVariables.funArgs false in
/--
Sort an array using `compare` to compare elements.
-/
def qsortOrd [ord : Ord α] (xs : Array α) : Array α :=
xs.qsort fun x y => compare x y |>.isLT
end Array end Array

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