Compare commits

..

1 Commits

Author SHA1 Message Date
Leonardo de Moura
d51ed25840 feat: BitVec simprocs 2024-02-19 13:33:11 -08:00
637 changed files with 2568 additions and 13399 deletions

View File

@@ -1,26 +0,0 @@
name: Check for modules that should use `prelude`
on: [pull_request]
jobs:
check-prelude:
runs-on: ubuntu-latest
steps:
- 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 }}
sparse-checkout: src/Lean
- name: Check Prelude
run: |
failed_files=""
while IFS= read -r -d '' file; do
if ! grep -q "^prelude$" "$file"; then
failed_files="$failed_files$file\n"
fi
done < <(find src/Lean -name '*.lean' -print0)
if [ -n "$failed_files" ]; then
echo -e "The following files should use 'prelude':\n$failed_files"
exit 1
fi

View File

@@ -410,8 +410,7 @@ jobs:
run: |
cd build
ulimit -c unlimited # coredumps
# clean rebuild in case of Makefile changes
make update-stage0 && rm -rf ./stage* && make -j4
make update-stage0 && make -j4
if: matrix.name == 'Linux' && needs.configure.outputs.quick == 'false'
- name: CCache stats
run: ccache -s
@@ -422,21 +421,19 @@ jobs:
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() && matrix.os == 'ubuntu-latest' }}
# 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
- name: Upload coredumps
uses: actions/upload-artifact@v3
if: ${{ failure() && matrix.os == 'ubuntu-latest' }}
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 can be made the “required” job, instead of listing each

View File

@@ -1,20 +0,0 @@
name: Check for copyright header
on: [pull_request]
jobs:
check-lean-files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Verify .lean files start with a copyright header.
run: |
FILES=$(find . -type d \( -path "./tests" -o -path "./doc" -o -path "./src/lake/examples" -o -path "./src/lake/tests" -o -path "./build" -o -path "./nix" \) -prune -o -type f -name "*.lean" -exec perl -ne 'BEGIN { $/ = undef; } print "$ARGV\n" if !m{\A/-\nCopyright}; exit;' {} \;)
if [ -n "$FILES" ]; then
echo "Found .lean files which do not have a copyright header:"
echo "$FILES"
exit 1
else
echo "All copyright headers present."
fi

View File

@@ -151,7 +151,7 @@ jobs:
echo "but 'git merge-base origin/master HEAD' reported: $MERGE_BASE_SHA"
git -C lean4.git log -10 origin/master
MESSAGE="- ❗ Std/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch. Try \`git rebase $MERGE_BASE_SHA --onto $NIGHTLY_SHA\`."
MESSAGE="- ❗ Std/Mathlib CI will not be attempted unless your PR branches off the \`nightly-with-mathlib\` branch."
fi
if [[ -n "$MESSAGE" ]]; then

View File

@@ -18,10 +18,6 @@ v4.7.0 (development in progress)
* `pp.proofs.withType` is now set to false by default to reduce noise in the info view.
* The pretty printer for applications now handles the case of over-application itself when applying app unexpanders.
In particular, the ``| `($_ $a $b $xs*) => `(($a + $b) $xs*)`` case of an `app_unexpander` is no longer necessary.
[#3495](https://github.com/leanprover/lean4/pull/3495).
* New `simp` (and `dsimp`) configuration option: `zetaDelta`. It is `false` by default.
The `zeta` option is still `true` by default, but their meaning has changed.
- When `zeta := true`, `simp` and `dsimp` reduce terms of the form
@@ -30,7 +26,7 @@ v4.7.0 (development in progress)
the context. For example, suppose the context contains `x := val`. Then,
any occurrence of `x` is replaced with `val`.
See [issue #2682](https://github.com/leanprover/lean4/pull/2682) for additional details. Here are some examples:
See issue [#2682](https://github.com/leanprover/lean4/pull/2682) for additional details. Here are some examples:
```
example (h : z = 9) : let x := 5; let y := 4; x + y = z := by
intro x
@@ -71,7 +67,7 @@ v4.7.0 (development in progress)
```
* When adding new local theorems to `simp`, the system assumes that the function application arguments
have been annotated with `no_index`. This modification, which addresses [issue #2670](https://github.com/leanprover/lean4/issues/2670),
have been annotated with `no_index`. This modification, which addresses issue [#2670](https://github.com/leanprover/lean4/issues/2670),
restores the Lean 3 behavior that users expect. With this modification, the following examples are now operational:
```
example {α β : Type} {f : α × β → β → β} (h : ∀ p : α × β, f p p.2 = p.2)
@@ -85,30 +81,6 @@ v4.7.0 (development in progress)
In both cases, `h` is applicable because `simp` does not index f-arguments anymore when adding `h` to the `simp`-set.
It's important to note, however, that global theorems continue to be indexed in the usual manner.
* Improved the error messages produced by the `decide` tactic. [#3422](https://github.com/leanprover/lean4/pull/3422)
* Improved auto-completion performance. [#3460](https://github.com/leanprover/lean4/pull/3460)
* Improved initial language server startup performance. [#3552](https://github.com/leanprover/lean4/pull/3552)
* Changed call hierarchy to sort entries and strip private header from names displayed in the call hierarchy. [#3482](https://github.com/leanprover/lean4/pull/3482)
* There is now a low-level error recovery combinator in the parsing framework, primarily intended for DSLs. [#3413](https://github.com/leanprover/lean4/pull/3413)
* The Library search `exact?` and `apply?` tactics that were originally in
* The library search tactics `exact?` and `apply?` that were originally in
Mathlib are now available in Lean itself. These use the implementation using
lazy discrimination trees from `Std`, and thus do not require a disk cache but
have a slightly longer startup time. The order used for selection lemmas has
changed as well to favor goals purely based on how many terms in the head
pattern match the current goal.
* The `solve_by_elim` tactic has been ported from `Std` to Lean so that library
search can use it.
* New `#check_tactic` and `#check_simp` commands have been added. These are
useful for checking tactics (particularly `simp`) behave as expected in test
suites.
Breaking changes:
* `Lean.withTraceNode` and variants got a stronger `MonadAlwaysExcept` assumption to
fix trace trees not being built on elaboration runtime exceptions. Instances for most elaboration
@@ -118,67 +90,67 @@ v4.6.0
---------
* Add custom simplification procedures (aka `simproc`s) to `simp`. Simprocs can be triggered by the simplifier on a specified term-pattern. Here is an small example:
```lean
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
```lean
import Lean.Meta.Tactic.Simp.BuiltinSimprocs.Nat
def foo (x : Nat) : Nat :=
x + 10
def foo (x : Nat) : Nat :=
x + 10
/--
The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`.
-/
simproc reduceFoo (foo _) :=
/- A term of type `Expr → SimpM Step -/
fun e => do
/-
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
* The constructor `.done` instructs `simp` that the result does
not need to be simplied further.
* The constructor `.visit` instructs `simp` to visit the resulting expression.
* The constructor `.continue` instructs `simp` to try other simplification procedures.
All three constructors take a `Result`. The `.continue` contructor may also take `none`.
`Result` has two fields `expr` (the new expression), and `proof?` (an optional proof).
If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`.
-/
/- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/
unless e.isAppOfArity ``foo 1 do
return .continue
/- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/
let some n ← Nat.fromExpr? e.appArg!
| return .continue
return .done { expr := Lean.mkNatLit (n+10) }
```
We disable simprocs support by using the command `set_option simprocs false`. This command is particularly useful when porting files to v4.6.0.
Simprocs can be scoped, manually added to `simp` commands, and suppressed using `-`. They are also supported by `simp?`. `simp only` does not execute any `simproc`. Here are some examples for the `simproc` defined above.
```lean
example : x + foo 2 = 12 + x := by
set_option simprocs false in
/- This `simp` command does not make progress since `simproc`s are disabled. -/
fail_if_success simp
simp_arith
example : x + foo 2 = 12 + x := by
/- `simp only` must not use the default simproc set. -/
fail_if_success simp only
simp_arith
example : x + foo 2 = 12 + x := by
/--
The `simproc` `reduceFoo` is invoked on terms that match the pattern `foo _`.
-/
simproc reduceFoo (foo _) :=
/- A term of type `Expr → SimpM Step -/
fun e => do
/-
`simp only` does not use the default simproc set,
but we can provide simprocs as arguments. -/
simp only [reduceFoo]
simp_arith
The `Step` type has three constructors: `.done`, `.visit`, `.continue`.
* The constructor `.done` instructs `simp` that the result does
not need to be simplied further.
* The constructor `.visit` instructs `simp` to visit the resulting expression.
* The constructor `.continue` instructs `simp` to try other simplification procedures.
example : x + foo 2 = 12 + x := by
/- We can use `-` to disable `simproc`s. -/
fail_if_success simp [-reduceFoo]
simp_arith
```
The command `register_simp_attr <id>` now creates a `simp` **and** a `simproc` set with the name `<id>`. The following command instructs Lean to insert the `reduceFoo` simplification procedure into the set `my_simp`. If no set is specified, Lean uses the default `simp` set.
```lean
simproc [my_simp] reduceFoo (foo _) := ...
```
All three constructors take a `Result`. The `.continue` contructor may also take `none`.
`Result` has two fields `expr` (the new expression), and `proof?` (an optional proof).
If the new expression is definitionally equal to the input one, then `proof?` can be omitted or set to `none`.
-/
/- `simp` uses matching modulo reducibility. So, we ensure the term is a `foo`-application. -/
unless e.isAppOfArity ``foo 1 do
return .continue
/- `Nat.fromExpr?` tries to convert an expression into a `Nat` value -/
let some n ← Nat.fromExpr? e.appArg!
| return .continue
return .done { expr := Lean.mkNatLit (n+10) }
```
We disable simprocs support by using the command `set_option simprocs false`. This command is particularly useful when porting files to v4.6.0.
Simprocs can be scoped, manually added to `simp` commands, and suppressed using `-`. They are also supported by `simp?`. `simp only` does not execute any `simproc`. Here are some examples for the `simproc` defined above.
```lean
example : x + foo 2 = 12 + x := by
set_option simprocs false in
/- This `simp` command does not make progress since `simproc`s are disabled. -/
fail_if_success simp
simp_arith
example : x + foo 2 = 12 + x := by
/- `simp only` must not use the default simproc set. -/
fail_if_success simp only
simp_arith
example : x + foo 2 = 12 + x := by
/-
`simp only` does not use the default simproc set,
but we can provide simprocs as arguments. -/
simp only [reduceFoo]
simp_arith
example : x + foo 2 = 12 + x := by
/- We can use `-` to disable `simproc`s. -/
fail_if_success simp [-reduceFoo]
simp_arith
```
The command `register_simp_attr <id>` now creates a `simp` **and** a `simproc` set with the name `<id>`. The following command instructs Lean to insert the `reduceFoo` simplification procedure into the set `my_simp`. If no set is specified, Lean uses the default `simp` set.
```lean
simproc [my_simp] reduceFoo (foo _) := ...
```
* The syntax of the `termination_by` and `decreasing_by` termination hints is overhauled:
@@ -317,7 +289,7 @@ v4.6.0
and hence greatly reduces the reliance on costly structure eta reduction. This has a large impact on mathlib,
reducing total CPU instructions by 3% and enabling impactful refactors like leanprover-community/mathlib4#8386
which reduces the build time by almost 20%.
See [PR #2478](https://github.com/leanprover/lean4/pull/2478) and [RFC #2451](https://github.com/leanprover/lean4/issues/2451).
See PR [#2478](https://github.com/leanprover/lean4/pull/2478) and RFC [#2451](https://github.com/leanprover/lean4/issues/2451).
* Add pretty printer settings to omit deeply nested terms (`pp.deepTerms false` and `pp.deepTerms.threshold`) ([PR #3201](https://github.com/leanprover/lean4/pull/3201))
@@ -336,7 +308,7 @@ Other improvements:
* produce simpler proof terms in `rw` [#3121](https://github.com/leanprover/lean4/pull/3121)
* fuse nested `mkCongrArg` calls in proofs generated by `simp` [#3203](https://github.com/leanprover/lean4/pull/3203)
* `induction using` followed by a general term [#3188](https://github.com/leanprover/lean4/pull/3188)
* allow generalization in `let` [#3060](https://github.com/leanprover/lean4/pull/3060), fixing [#3065](https://github.com/leanprover/lean4/issues/3065)
* allow generalization in `let` [#3060](https://github.com/leanprover/lean4/pull/3060, fixing [#3065](https://github.com/leanprover/lean4/issues/3065)
* reducing out-of-bounds `swap!` should return `a`, not `default`` [#3197](https://github.com/leanprover/lean4/pull/3197), fixing [#3196](https://github.com/leanprover/lean4/issues/3196)
* derive `BEq` on structure with `Prop`-fields [#3191](https://github.com/leanprover/lean4/pull/3191), fixing [#3140](https://github.com/leanprover/lean4/issues/3140)
* refine through more `casesOnApp`/`matcherApp` [#3176](https://github.com/leanprover/lean4/pull/3176), fixing [#3175](https://github.com/leanprover/lean4/pull/3175)

View File

@@ -74,9 +74,3 @@ Lean's build process uses [`ccache`](https://ccache.dev/) if it is
installed to speed up recompilation of the generated C code. Without
`ccache`, you'll likely spend more time than necessary waiting on
rebuilds - it's a good idea to make sure it's installed.
### `prelude`
Unlike most Lean projects, all submodules of the `Lean` module begin with the
`prelude` keyword. This disables the automated import of `Init`, meaning that
developers need to figure out their own subset of `Init` to import. This is done
such that changing files in `Init` doesn't force a full rebuild of `Lean`.

View File

@@ -33,7 +33,7 @@ convert the pure non-monadic value `x / y` into the required `Except` object. S
Now this return typing would get tedious if you had to include it everywhere that you call this
function, however, Lean type inference can clean this up. For example, you can define a test
function that calls the `divide` function and you don't need to say anything here about the fact that
function can calls the `divide` function and you don't need to say anything here about the fact that
it might throw an error, because that is inferred:
-/
def test := divide 5 0

View File

@@ -65,7 +65,12 @@ rec {
installPhase = ''
mkdir -p $out/bin $out/lib/lean
mv bin/lean $out/bin/
mv lib/lean/*.so $out/lib/lean
mv lib/lean/libleanshared.* $out/lib/lean
'' + lib.optionalString stdenv.isDarwin ''
for lib in $(otool -L $out/bin/lean | tail -n +2 | cut -d' ' -f1); do
if [[ "$lib" == *lean* ]]; then install_name_tool -change "$lib" "$out/lib/lean/$(basename $lib)" $out/bin/lean; fi
done
otool -L $out/bin/lean
'';
meta.mainProgram = "lean";
});
@@ -115,35 +120,29 @@ rec {
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" ]; };
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}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
-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) \
-o $out/$libName
'';
leanshared = runCommand "leanshared" { buildInputs = [ stdenv.cc ]; libName = "libleanshared${stdenv.hostPlatform.extensions.sharedLibrary}"; } ''
mkdir $out
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared -Wl,-Bsymbolic \
${libInit_shared}/* -Wl,--whole-archive -lLean -lleancpp -Wl,--no-whole-archive -lstdc++ -lm ${stdlibLinkFlags} \
LEAN_CC=${stdenv.cc}/bin/cc ${lean-bin-tools-unwrapped}/bin/leanc -shared ${lib.optionalString stdenv.isLinux "-Wl,-Bsymbolic"} \
${if stdenv.isDarwin then "-Wl,-force_load,${Init.staticLib}/libInit.a -Wl,-force_load,${Lean.staticLib}/libLean.a -Wl,-force_load,${leancpp}/lib/lean/libleancpp.a ${leancpp}/lib/libleanrt_initial-exec.a -lc++"
else "-Wl,--whole-archive -lInit -lLean -lleancpp ${leancpp}/lib/libleanrt_initial-exec.a -Wl,--no-whole-archive -lstdc++"} -lm ${stdlibLinkFlags} \
$(${llvmPackages.libllvm.dev}/bin/llvm-config --ldflags --libs) \
-o $out/$libName
'';
mods = foldl' (mods: pkg: mods // pkg.mods) {} stdlib;
print-paths = Lean.makePrintPathsFor [] mods;
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} "$@"
LEAN_CC=${stdenv.cc}/bin/cc ${Leanc.executable}/bin/leanc -I${lean-bin-tools-unwrapped}/include ${stdlibLinkFlags} -L${leanshared} "$@"
'';
lean = runCommand "lean" { buildInputs = lib.optional stdenv.isDarwin darwin.cctools; } ''
mkdir -p $out/bin
${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${libInit_shared}/* ${leanshared}/* -o $out/bin/lean
${leanc}/bin/leanc ${leancpp}/lib/lean.cpp.o ${leanshared}/* -o $out/bin/lean
'';
# derivation following the directory layout of the "basic" setup, mostly useful for running tests
lean-all = stdenv.mkDerivation {
name = "lean-${desc}";
buildCommand = ''
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}/* $out/lib/lean/
ln -sf ${leancpp}/lib/lean/* ${lib.concatMapStringsSep " " (l: "${l.modRoot}/* ${l.staticLib}/*") (lib.reverseList stdlib)} ${leanshared}/* $out/lib/lean/
# 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
# NOTE: `lndir` will not override existing `bin/leanc`

View File

@@ -10,7 +10,7 @@ lib.makeOverridable (
staticLibDeps ? [],
# Whether to wrap static library inputs in a -Wl,--start-group [...] -Wl,--end-group to ensure dependencies are resolved.
groupStaticLibs ? false,
# Shared library dependencies included at interpretation with --load-dynlib and linked to. Each derivation `shared` should contain a
# Shared library dependencies included at interpretation with --load-dynlib and linked to. Each derivation `shared` should contain a
# shared library at the path `${shared}/${shared.libName or shared.name}` and a name to link to like `-l${shared.linkName or shared.name}`.
# These libs are also linked to in packages that depend on this one.
nativeSharedLibs ? [],
@@ -88,9 +88,9 @@ with builtins; let
allNativeSharedLibs =
lib.unique (lib.flatten (nativeSharedLibs ++ (map (dep: dep.allNativeSharedLibs or []) allExternalDeps)));
# A flattened list of all static library dependencies: this and every dep module's explicitly provided `staticLibDeps`,
# A flattened list of all static library dependencies: this and every dep module's explicitly provided `staticLibDeps`,
# plus every dep module itself: `dep.staticLib`
allStaticLibDeps =
allStaticLibDeps =
lib.unique (lib.flatten (staticLibDeps ++ (map (dep: [dep.staticLib] ++ dep.staticLibDeps or []) allExternalDeps)));
pathOfSharedLib = dep: dep.libPath or "${dep}/${dep.libName or dep.name}";
@@ -249,7 +249,7 @@ in rec {
${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)}'';
executable = lib.makeOverridable ({ withSharedStdlib ? true }: let
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.libInit_shared}/* ${lean-final.leanshared}/*";
objPaths = map (drv: "${drv}/${drv.oPath}") (attrValues objects) ++ lib.optional withSharedStdlib "${lean-final.leanshared}/*";
in runCommand executableName { buildInputs = [ stdenv.cc leanc ]; } ''
mkdir -p $out/bin
leanc ${staticLibLinkWrapper (lib.concatStringsSep " " (objPaths ++ map (d: "${d}/*.a") allStaticLibDeps))} \

View File

@@ -1,8 +1,3 @@
/-
Copyright (c) 2022 Sebastian Ullrich. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Ullrich
-/
import Lean.Runtime
abbrev M := ReaderT IO.FS.Stream IO
@@ -21,7 +16,7 @@ def mkTypedefFn (i : Nat) : M Unit := do
emit s!"typedef obj* (*fn{i})({args}); // NOLINT\n"
emit s!"#define FN{i}(f) reinterpret_cast<fn{i}>(lean_closure_fun(f))\n"
def genSeq (n : Nat) (f : Nat String) (sep := ", ") : String :=
def genSeq (n : Nat) (f : Nat String) (sep := ", ") : String :=
List.range n |>.map f |>.intersperse sep |> .join
-- make string: "obj* a1, obj* a2, ..., obj* an"

View File

@@ -25,8 +25,6 @@ cp -L llvm/bin/llvm-ar stage1/bin/
# dependencies of the above
$CP llvm/lib/lib{clang-cpp,LLVM}*.so* stage1/lib/
$CP $ZLIB/lib/libz.so* stage1/lib/
# general clang++ dependency, breaks cross-library C++ exceptions if linked statically
$CP $GCC_LIB/lib/libgcc_s.so* stage1/lib/
# bundle libatomic (referenced by LLVM >= 15, and required by the lean executable to run)
$CP $GCC_LIB/lib/libatomic.so* stage1/lib/
@@ -62,7 +60,7 @@ fi
# 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
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 -Wl,--as-needed -Wl,-Bstatic -lgmp -lunwind -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 -static-libgcc -Wl,-Bstatic -lgmp -lunwind -Wl,-Bdynamic -Wl,--no-as-needed -fuse-ld=lld'"
# when not using the above flags, link GMP dynamically/as usual
echo -n " -DLEAN_EXTRA_LINKER_FLAGS='-Wl,--as-needed -lgmp -Wl,--no-as-needed'"
# do not set `LEAN_CC` for tests

View File

@@ -299,12 +299,13 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
cmake_path(GET ZLIB_LIBRARY PARENT_PATH ZLIB_LIBRARY_PARENT_PATH)
string(APPEND LEANSHARED_LINKER_FLAGS " -L ${ZLIB_LIBRARY_PARENT_PATH}")
endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lleanrt")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lleancpp -lInit -lLean -lnodefs.js -lleanrt")
else()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
string(APPEND LEANC_STATIC_LINKER_FLAGS " -Wl,--start-group -lleancpp -lLean -Wl,--end-group -Wl,--start-group -lInit -lleanrt -Wl,--end-group")
endif()
string(APPEND LEANC_STATIC_LINKER_FLAGS " -lLake")
set(LEAN_CXX_STDLIB "-lstdc++" CACHE STRING "C++ stdlib linker flags")
@@ -312,11 +313,8 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set(LEAN_CXX_STDLIB "-lc++")
endif()
string(APPEND TOOLCHAIN_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
# flags for user binaries = flags for toolchain binaries + Lake
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${TOOLCHAIN_STATIC_LINKER_FLAGS} -lLake")
string(APPEND LEANC_STATIC_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
string(APPEND LEANSHARED_LINKER_FLAGS " ${LEAN_CXX_STDLIB}")
if (LLVM)
string(APPEND LEANSHARED_LINKER_FLAGS " -L${LLVM_CONFIG_LIBDIR} ${LLVM_CONFIG_LDFLAGS} ${LLVM_CONFIG_LIBS} ${LLVM_CONFIG_SYSTEM_LIBS}")
@@ -344,9 +342,9 @@ endif()
# get rid of unused parts of C++ stdlib
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-dead_strip")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-dead_strip")
elseif(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,--gc-sections")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,--gc-sections")
endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
@@ -356,20 +354,26 @@ endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
if(BSYMBOLIC)
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-Bsymbolic")
endif()
string(APPEND CMAKE_CXX_FLAGS " -fPIC -ftls-model=initial-exec")
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
string(APPEND TOOLCHAIN_SHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,-rpath=\\$$ORIGIN/..:\\$$ORIGIN")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lleanshared -Wl,-rpath=\\\$ORIGIN/../lib:\\\$ORIGIN/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
string(APPEND CMAKE_CXX_FLAGS " -ftls-model=initial-exec")
string(APPEND INIT_SHARED_LINKER_FLAGS " -install_name @rpath/libInit_shared.dylib")
string(APPEND LEANSHARED_LINKER_FLAGS " -install_name @rpath/libleanshared.dylib")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lleanshared -Wl,-rpath,@executable_path/../lib -Wl,-rpath,@executable_path/../lib/lean")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
string(APPEND CMAKE_CXX_FLAGS " -fPIC")
string(APPEND LEANC_EXTRA_FLAGS " -fPIC")
# 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
# 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".
string(APPEND CMAKE_EXE_LINKER_FLAGS " -Wl,--whole-archive -lInit -lLean -lleancpp -lleanrt ${EMSCRIPTEN_SETTINGS} -lnodefs.js -s EXIT_RUNTIME=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1")
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lleanshared")
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
@@ -395,7 +399,7 @@ endif()
# are already loaded) and probably fail unless we set up LD_LIBRARY_PATH.
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# import library created by the `leanshared` target
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lInit_shared -lleanshared")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -lleanshared")
elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
string(APPEND LEANC_SHARED_LINKER_FLAGS " -Wl,-undefined,dynamic_lookup")
endif()
@@ -501,25 +505,13 @@ string(REGEX REPLACE "^([a-zA-Z]):" "/\\1" LEAN_BIN "${CMAKE_BINARY_DIR}/bin")
# (also looks nicer in the build log)
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 -lInit ${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 -lLean -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")
# 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
# 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".
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")
if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
string(APPEND LEANSHARED_LINKER_FLAGS " -Wl,--out-implib,${CMAKE_BINARY_DIR}/lib/lean/libleanshared.dll.a")
endif()
endif()
# Build the compiler using the bootstrapped C sources for stage0, and use
@@ -528,6 +520,10 @@ if (LLVM AND ${STAGE} GREATER 0)
set(EXTRA_LEANMAKE_OPTS "LLVM=1")
endif()
# Escape for `make`. Yes, twice.
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
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)
add_custom_target(make_stdlib ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
# The actual rule is in a separate makefile because we want to prefix it with '+' to use the Make job server
@@ -545,33 +541,13 @@ endif()
# We declare these as separate custom targets so they use separate `make` invocations, which makes `make` recompute which dependencies
# (e.g. `libLean.a`) are now newer than the target file
if(${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
# dummy targets, see `MAIN_MODULE` discussion above
add_custom_target(Init_shared ALL
DEPENDS make_stdlib leanrt_initial-exec
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libInit_shared${CMAKE_SHARED_LIBRARY_SUFFIX}
)
add_custom_target(leanshared ALL
DEPENDS Init_shared leancpp
COMMAND touch ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libleanshared${CMAKE_SHARED_LIBRARY_SUFFIX}
)
else()
add_custom_target(Init_shared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS make_stdlib leanrt_initial-exec
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init_shared
VERBATIM)
add_custom_target(leanshared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS make_stdlib leancpp leanrt_initial-exec
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
VERBATIM)
add_custom_target(leanshared ALL
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS Init_shared leancpp
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make leanshared
VERBATIM)
string(APPEND CMAKE_EXE_LINKER_FLAGS " -lInit_shared -lleanshared")
endif()
if(${STAGE} GREATER 0 AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(${STAGE} GREATER 0)
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()
@@ -592,7 +568,7 @@ endif()
# use Bash version for building, use Lean version in bin/ for tests & distribution
configure_file("${LEAN_SOURCE_DIR}/bin/leanc.in" "${CMAKE_BINARY_DIR}/leanc.sh" @ONLY)
if(${STAGE} GREATER 0 AND EXISTS ${LEAN_SOURCE_DIR}/Leanc.lean AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
if(${STAGE} GREATER 0 AND EXISTS ${LEAN_SOURCE_DIR}/Leanc.lean)
configure_file("${LEAN_SOURCE_DIR}/Leanc.lean" "${CMAKE_BINARY_DIR}/leanc/Leanc.lean" @ONLY)
add_custom_target(leanc ALL
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/leanc
@@ -643,8 +619,3 @@ if(LEAN_INSTALL_PREFIX)
set(LEAN_INSTALL_SUFFIX "-${LOWER_SYSTEM_NAME}" CACHE STRING "If LEAN_INSTALL_PREFIX is set, append this value to CMAKE_INSTALL_PREFIX")
set(CMAKE_INSTALL_PREFIX "${LEAN_INSTALL_PREFIX}/lean-${LEAN_VERSION_STRING}${LEAN_INSTALL_SUFFIX}")
endif()
# Escape for `make`. Yes, twice.
string(REPLACE "$" "$$" CMAKE_EXE_LINKER_FLAGS_MAKE "${CMAKE_EXE_LINKER_FLAGS}")
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)

View File

@@ -1,5 +1,5 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Mario Carneiro
-/

View File

@@ -321,7 +321,7 @@ Helper definition used by the elaborator. It is not meant to be used directly by
This is used for coercions between monads, in the case where we want to apply
a monad lift and a coercion on the result type at the same time.
-/
@[coe_decl] abbrev Lean.Internal.liftCoeM {m : Type u Type v} {n : Type u Type w} {α β : Type u}
@[inline, coe_decl] def Lean.Internal.liftCoeM {m : Type u Type v} {n : Type u Type w} {α β : Type u}
[MonadLiftT m n] [ a, CoeT α a β] [Monad n] (x : m α) : n β := do
let a liftM x
pure (CoeT.coe a)
@@ -331,7 +331,7 @@ Helper definition used by the elaborator. It is not meant to be used directly by
This is used for coercing the result type under a monad.
-/
@[coe_decl] abbrev Lean.Internal.coeM {m : Type u Type v} {α β : Type u}
@[inline, coe_decl] def Lean.Internal.coeM {m : Type u Type v} {α β : Type u}
[ a, CoeT α a β] [Monad m] (x : m α) : m β := do
let a x
pure (CoeT.coe a)

View File

@@ -6,7 +6,7 @@ Authors: Leonardo de Moura
Notation for operators defined at Prelude.lean
-/
prelude
import Init.Meta
import Init.NotationExtra
namespace Lean.Parser.Tactic.Conv
@@ -308,7 +308,4 @@ Basic forms:
-- refer to the syntax category instead of this syntax
syntax (name := conv) "conv" (" at " ident)? (" in " (occs)? term)? " => " convSeq : tactic
/-- `norm_cast` tactic in `conv` mode. -/
syntax (name := normCast) "norm_cast" : conv
end Lean.Parser.Tactic.Conv

View File

@@ -32,5 +32,3 @@ import Init.Data.Prod
import Init.Data.AC
import Init.Data.Queue
import Init.Data.Channel
import Init.Data.Cast
import Init.Data.Sum

View File

@@ -185,84 +185,3 @@ theorem anyM_stop_le_start [Monad m] (p : α → m Bool) (as : Array α) (start
theorem mem_def (a : α) (as : Array α) : a as a as.data :=
fun | .mk h => h, Array.Mem.mk
/-- # get -/
@[simp] theorem get_eq_getElem (a : Array α) (i : Fin _) : a.get i = a[i.1] := rfl
theorem getElem?_lt
(a : Array α) {i : Nat} (h : i < a.size) : a[i]? = some (a[i]) := dif_pos h
theorem getElem?_ge
(a : Array α) {i : Nat} (h : i a.size) : a[i]? = none := dif_neg (Nat.not_lt_of_le h)
@[simp] theorem get?_eq_getElem? (a : Array α) (i : Nat) : a.get? i = a[i]? := rfl
theorem getElem?_len_le (a : Array α) {i : Nat} (h : a.size i) : a[i]? = none := by
simp [getElem?_ge, h]
theorem getD_get? (a : Array α) (i : Nat) (d : α) :
Option.getD a[i]? d = if p : i < a.size then a[i]'p else d := by
if h : i < a.size then
simp [setD, h, getElem?]
else
have p : i a.size := Nat.le_of_not_gt h
simp [setD, getElem?_len_le _ p, h]
@[simp] theorem getD_eq_get? (a : Array α) (n d) : a.getD n d = (a[n]?).getD d := by
simp only [getD, get_eq_getElem, get?_eq_getElem?]; split <;> simp [getD_get?, *]
theorem get!_eq_getD [Inhabited α] (a : Array α) : a.get! n = a.getD n default := rfl
@[simp] theorem get!_eq_getElem? [Inhabited α] (a : Array α) (i : Nat) : a.get! i = (a.get? i).getD default := by
by_cases p : i < a.size <;> simp [getD_get?, get!_eq_getD, p]
/-- # set -/
@[simp] theorem getElem_set_eq (a : Array α) (i : Fin a.size) (v : α) {j : Nat}
(eq : i.val = j) (p : j < (a.set i v).size) :
(a.set i v)[j]'p = v := by
simp [set, getElem_eq_data_get, eq]
@[simp] theorem getElem_set_ne (a : Array α) (i : Fin a.size) (v : α) {j : Nat} (pj : j < (a.set i v).size)
(h : i.val j) : (a.set i v)[j]'pj = a[j]'(size_set a i v pj) := by
simp only [set, getElem_eq_data_get, List.get_set_ne _ h]
theorem getElem_set (a : Array α) (i : Fin a.size) (v : α) (j : Nat)
(h : j < (a.set i v).size) :
(a.set i v)[j]'h = if i = j then v else a[j]'(size_set a i v h) := by
by_cases p : i.1 = j <;> simp [p]
@[simp] theorem getElem?_set_eq (a : Array α) (i : Fin a.size) (v : α) :
(a.set i v)[i.1]? = v := by simp [getElem?_lt, i.2]
@[simp] theorem getElem?_set_ne (a : Array α) (i : Fin a.size) {j : Nat} (v : α)
(ne : i.val j) : (a.set i v)[j]? = a[j]? := by
by_cases h : j < a.size <;> simp [getElem?_lt, getElem?_ge, Nat.ge_of_not_lt, ne, h]
/- # setD -/
@[simp] theorem set!_is_setD : @set! = @setD := rfl
@[simp] theorem size_setD (a : Array α) (index : Nat) (val : α) :
(Array.setD a index val).size = a.size := by
if h : index < a.size then
simp [setD, h]
else
simp [setD, h]
@[simp] theorem getElem_setD_eq (a : Array α) {i : Nat} (v : α) (h : _) :
(setD a i v)[i]'h = v := by
simp at h
simp only [setD, h, dite_true, getElem_set, ite_true]
@[simp]
theorem getElem?_setD_eq (a : Array α) {i : Nat} (p : i < a.size) (v : α) : (a.setD i v)[i]? = some v := by
simp [getElem?_lt, p]
/-- Simplifies a normal form from `get!` -/
@[simp] theorem getD_get?_setD (a : Array α) (i : Nat) (v d : α) :
Option.getD (setD a i v)[i]? d = if i < a.size then v else d := by
by_cases h : i < a.size <;>
simp [setD, Nat.not_lt_of_le, h, getD_get?]
end Array

View File

@@ -8,6 +8,16 @@ import Init.Data.Array.Basic
import Init.Data.Nat.Linear
import Init.Data.List.BasicAux
theorem List.sizeOf_get_lt [SizeOf α] (as : List α) (i : Fin as.length) : sizeOf (as.get i) < sizeOf as := by
match as, i with
| [], i => apply Fin.elim0 i
| a::as, 0, _ => simp_arith [get]
| a::as, i+1, h =>
simp [get]
have h : i < as.length := Nat.lt_of_succ_lt_succ h
have ih := sizeOf_get_lt as i, h
exact Nat.lt_of_lt_of_le ih (Nat.le_add_left ..)
namespace Array
/-- `a ∈ as` is a predicate which asserts that `a` is in the array `as`. -/
@@ -19,6 +29,10 @@ structure Mem (a : α) (as : Array α) : Prop where
instance : Membership α (Array α) where
mem a as := Mem a as
theorem sizeOf_get_lt [SizeOf α] (as : Array α) (i : Fin as.size) : sizeOf (as.get i) < sizeOf as := by
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_get_lt as i) (by simp_arith)
theorem sizeOf_lt_of_mem [SizeOf α] {as : Array α} (h : a as) : sizeOf a < sizeOf as := by
cases as with | _ as =>
exact Nat.lt_trans (List.sizeOf_lt_of_mem h.val) (by simp_arith)

View File

@@ -143,7 +143,6 @@ def toSubarray (as : Array α) (start : Nat := 0) (stop : Nat := as.size) : Suba
else
{ as := as, start := as.size, stop := as.size, h₁ := Nat.le_refl _, h₂ := Nat.le_refl _ }
@[coe]
def ofSubarray (s : Subarray α) : Array α := Id.run do
let mut as := mkEmpty (s.stop - s.start)
for a in s do

View File

@@ -1,8 +1,3 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Data.BitVec.Basic
import Init.Data.BitVec.Bitblast

View File

@@ -1,5 +1,6 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Copyright (c) 2022 by the authors listed in the file AUTHORS and their
institutional affiliations. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix, Wojciech Nawrocki, Leonardo de Moura, Mario Carneiro, Alex Keizer
-/
@@ -8,6 +9,8 @@ import Init.Data.Fin.Basic
import Init.Data.Nat.Bitwise.Lemmas
import Init.Data.Nat.Power2
namespace Std
/-!
We define bitvectors. We choose the `Fin` representation over others for its relative efficiency
(Lean has special support for `Nat`), alignment with `UIntXY` types which are also represented
@@ -20,10 +23,8 @@ of SMT-LIBv2.
-/
/--
A bitvector of the specified width.
This is represented as the underlying `Nat` number in both the runtime
and the kernel, inheriting all the special support for `Nat`.
A bitvector of the specified width. This is represented as the underlying `Nat` number
in both the runtime and the kernel, inheriting all the special support for `Nat`.
-/
structure BitVec (w : Nat) where
/-- Construct a `BitVec w` from a number less than `2^w`.
@@ -32,38 +33,20 @@ structure BitVec (w : Nat) where
/-- Interpret a bitvector as a number less than `2^w`.
O(1), because we use `Fin` as the internal representation of a bitvector. -/
toFin : Fin (2^w)
@[deprecated] abbrev Std.BitVec := _root_.BitVec
-- We manually derive the `DecidableEq` instances for `BitVec` because
-- we want to have builtin support for bit-vector literals, and we
-- need a name for this function to implement `canUnfoldAtMatcher` at `WHNF.lean`.
def BitVec.decEq (a b : BitVec n) : Decidable (a = b) :=
match a, b with
| n, m =>
if h : n = m then
isTrue (h rfl)
else
isFalse (fun h' => BitVec.noConfusion h' (fun h' => absurd h' h))
instance : DecidableEq (BitVec n) := BitVec.decEq
deriving DecidableEq
namespace BitVec
section Nat
/-- `cast eq i` embeds `i` into an equal `BitVec` type. -/
@[inline] def cast (eq : n = m) (i : BitVec n) : BitVec m :=
.ofFin (Fin.cast (congrArg _ eq) i.toFin)
/-- The `BitVec` with value `i`, given a proof that `i < 2^n`. -/
@[match_pattern]
protected def ofNatLt {n : Nat} (i : Nat) (p : i < 2^n) : BitVec n where
toFin := i, p
/-- The `BitVec` with value `i mod 2^n`. -/
@[match_pattern]
/-- The `BitVec` with value `i mod 2^n`. Treated as an operation on bitvectors,
this is truncation of the high bits when downcasting and zero-extension when upcasting. -/
protected def ofNat (n : Nat) (i : Nat) : BitVec n where
toFin := Fin.ofNat' i (Nat.two_pow_pos n)
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
instance natCastInst : NatCast (BitVec w) := BitVec.ofNat w
instance : NatCast (BitVec w) := BitVec.ofNat w
/-- Given a bitvector `a`, return the underlying `Nat`. This is O(1) because `BitVec` is a
(zero-cost) wrapper around a `Nat`. -/
@@ -72,43 +55,6 @@ protected def toNat (a : BitVec n) : Nat := a.toFin.val
/-- Return the bound in terms of toNat. -/
theorem isLt (x : BitVec w) : x.toNat < 2^w := x.toFin.isLt
/-- Theorem for normalizing the bit vector literal representation. -/
-- TODO: This needs more usage data to assess which direction the simp should go.
@[simp, bv_toNat] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = .ofNat n i := rfl
-- Note. Mathlib would like this to go the other direction.
@[simp] theorem natCast_eq_ofNat (w x : Nat) : @Nat.cast (BitVec w) _ x = .ofNat w x := rfl
end Nat
section subsingleton
/-- All empty bitvectors are equal -/
instance : Subsingleton (BitVec 0) where
allEq := by intro 0, _ 0, _; rfl
/-- The empty bitvector -/
abbrev nil : BitVec 0 := 0
/-- Every bitvector of length 0 is equal to `nil`, i.e., there is only one empty bitvector -/
theorem eq_nil (x : BitVec 0) : x = nil := Subsingleton.allEq ..
end subsingleton
section zero_allOnes
/-- Return a bitvector `0` of size `n`. This is the bitvector with all zero bits. -/
protected def zero (n : Nat) : BitVec n := .ofNatLt 0 (Nat.two_pow_pos n)
instance : Inhabited (BitVec n) where default := .zero n
/-- Bit vector of size `n` where all bits are `1`s -/
def allOnes (n : Nat) : BitVec n :=
.ofNatLt (2^n - 1) (Nat.le_of_eq (Nat.sub_add_cancel (Nat.two_pow_pos n)))
end zero_allOnes
section getXsb
/-- Return the `i`-th least significant bit or `false` if `i ≥ w`. -/
@[inline] def getLsb (x : BitVec w) (i : Nat) : Bool := x.toNat.testBit i
@@ -118,67 +64,43 @@ section getXsb
/-- Return most-significant bit in bitvector. -/
@[inline] protected def msb (a : BitVec n) : Bool := getMsb a 0
end getXsb
section Int
/-- Interpret the bitvector as an integer stored in two's complement form. -/
protected def toInt (a : BitVec n) : Int :=
if 2 * a.toNat < 2^n then
a.toNat
else
(a.toNat : Int) - (2^n : Nat)
if a.msb then Int.ofNat a.toNat - Int.ofNat (2^n) else a.toNat
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
protected def ofInt (n : Nat) (i : Int) : BitVec n := .ofNatLt (i % (Int.ofNat (2^n))).toNat (by
apply (Int.toNat_lt _).mpr
· apply Int.emod_lt_of_pos
exact Int.ofNat_pos.mpr (Nat.two_pow_pos _)
· apply Int.emod_nonneg
intro eq
apply Nat.ne_of_gt (Nat.two_pow_pos n)
exact Int.ofNat_inj.mp eq)
/-- Return a bitvector `0` of size `n`. This is the bitvector with all zero bits. -/
protected def zero (n : Nat) : BitVec n := 0, Nat.two_pow_pos n
instance : IntCast (BitVec w) := BitVec.ofInt w
instance : Inhabited (BitVec n) where default := .zero n
end Int
section Syntax
instance instOfNat : OfNat (BitVec n) i where ofNat := .ofNat n i
/-- Notation for bit vector literals. `i#n` is a shorthand for `BitVec.ofNat n i`. -/
scoped syntax:max term:max noWs "#" noWs term:max : term
macro_rules | `($i#$n) => `(BitVec.ofNat $n $i)
/- Support for `i#n` notation in patterns. -/
attribute [match_pattern] BitVec.ofNat
/-- Unexpander for bit vector literals. -/
@[app_unexpander BitVec.ofNat] def unexpandBitVecOfNat : Lean.PrettyPrinter.Unexpander
| `($(_) $n $i) => `($i#$n)
| _ => throw ()
/-- Notation for bit vector literals without truncation. `i#'lt` is a shorthand for `BitVec.ofNatLt i lt`. -/
scoped syntax:max term:max noWs "#'" noWs term:max : term
macro_rules | `($i#'$p) => `(BitVec.ofNatLt $i $p)
/-- Unexpander for bit vector literals without truncation. -/
@[app_unexpander BitVec.ofNatLt] def unexpandBitVecOfNatLt : Lean.PrettyPrinter.Unexpander
| `($(_) $i $p) => `($i#'$p)
| _ => throw ()
end Syntax
section repr_toString
/-- Convert bitvector into a fixed-width hex number. -/
protected def toHex {n : Nat} (x : BitVec n) : String :=
let s := (Nat.toDigits 16 x.toNat).asString
let t := (List.replicate ((n+3) / 4 - s.length) '0').asString
t ++ s
instance : Repr (BitVec n) where reprPrec a _ := "0x" ++ (a.toHex : Std.Format) ++ "#" ++ repr n
instance : Repr (BitVec n) where reprPrec a _ := "0x" ++ (a.toHex : Format) ++ "#" ++ repr n
instance : ToString (BitVec n) where toString a := toString (repr a)
end repr_toString
section arithmetic
/-- Theorem for normalizing the bit vector literal representation. -/
-- TODO: This needs more usage data to assess which direction the simp should go.
@[simp] theorem ofNat_eq_ofNat : @OfNat.ofNat (BitVec n) i _ = BitVec.ofNat n i := rfl
@[simp] theorem natCast_eq_ofNat : Nat.cast x = x#w := rfl
/--
Addition for bit vectors. This can be interpreted as either signed or unsigned addition
@@ -186,14 +108,14 @@ modulo `2^n`.
SMT-Lib name: `bvadd`.
-/
protected def add (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + y.toNat)
protected def add (x y : BitVec n) : BitVec n where toFin := x.toFin + y.toFin
instance : Add (BitVec n) := BitVec.add
/--
Subtraction for bit vectors. This can be interpreted as either signed or unsigned subtraction
modulo `2^n`.
-/
protected def sub (x y : BitVec n) : BitVec n := .ofNat n (x.toNat + (2^n - y.toNat))
protected def sub (x y : BitVec n) : BitVec n where toFin := x.toFin - y.toFin
instance : Sub (BitVec n) := BitVec.sub
/--
@@ -202,9 +124,12 @@ modulo `2^n`.
SMT-Lib name: `bvneg`.
-/
protected def neg (x : BitVec n) : BitVec n := .ofNat n (2^n - x.toNat)
protected def neg (x : BitVec n) : BitVec n := .sub 0 x
instance : Neg (BitVec n) := .neg
/-- Bit vector of size `n` where all bits are `1`s -/
def allOnes (n : Nat) : BitVec n := -1
/--
Return the absolute value of a signed bitvector.
-/
@@ -216,14 +141,13 @@ modulo `2^n`.
SMT-Lib name: `bvmul`.
-/
protected def mul (x y : BitVec n) : BitVec n := BitVec.ofNat n (x.toNat * y.toNat)
protected def mul (x y : BitVec n) : BitVec n := ofFin <| x.toFin * y.toFin
instance : Mul (BitVec n) := .mul
/--
Unsigned division for bit vectors using the Lean convention where division by zero returns zero.
-/
def udiv (x y : BitVec n) : BitVec n :=
(x.toNat / y.toNat)#'(Nat.lt_of_le_of_lt (Nat.div_le_self _ _) x.isLt)
def udiv (x y : BitVec n) : BitVec n := ofFin <| x.toFin / y.toFin
instance : Div (BitVec n) := .udiv
/--
@@ -231,8 +155,7 @@ Unsigned modulo for bit vectors.
SMT-Lib name: `bvurem`.
-/
def umod (x y : BitVec n) : BitVec n :=
(x.toNat % y.toNat)#'(Nat.lt_of_le_of_lt (Nat.mod_le _ _) x.isLt)
def umod (x y : BitVec n) : BitVec n := ofFin <| x.toFin % y.toFin
instance : Mod (BitVec n) := .umod
/--
@@ -242,7 +165,7 @@ where division by zero returns the `allOnes` bitvector.
SMT-Lib name: `bvudiv`.
-/
def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then allOnes n else udiv x y
def smtUDiv (x y : BitVec n) : BitVec n := if y = 0 then -1 else .udiv x y
/--
Signed t-division for bit vectors using the Lean convention where division
@@ -295,54 +218,35 @@ SMT_Lib name: `bvsmod`.
-/
def smod (s t : BitVec m) : BitVec m :=
match s.msb, t.msb with
| false, false => umod s t
| false, false => .umod s t
| false, true =>
let u := umod s (.neg t)
(if u = .zero m then u else .add u t)
let u := .umod s (.neg t)
(if u = BitVec.ofNat m 0 then u else .add u t)
| true, false =>
let u := umod (.neg s) t
(if u = .zero m then u else .sub t u)
| true, true => .neg (umod (.neg s) (.neg t))
end arithmetic
section bool
/-- Turn a `Bool` into a bitvector of length `1` -/
def ofBool (b : Bool) : BitVec 1 := cond b 1 0
@[simp] theorem ofBool_false : ofBool false = 0 := by trivial
@[simp] theorem ofBool_true : ofBool true = 1 := by trivial
/-- Fills a bitvector with `w` copies of the bit `b`. -/
def fill (w : Nat) (b : Bool) : BitVec w := bif b then -1 else 0
end bool
section relations
let u := .umod (.neg s) t
(if u = BitVec.ofNat m 0 then u else .sub t u)
| true, true => .neg (.umod (.neg s) (.neg t))
/--
Unsigned less-than for bit vectors.
SMT-Lib name: `bvult`.
-/
protected def ult (x y : BitVec n) : Bool := x.toNat < y.toNat
instance : LT (BitVec n) where lt := (·.toNat < ·.toNat)
protected def ult (x y : BitVec n) : Bool := x.toFin < y.toFin
instance : LT (BitVec n) where lt x y := x.toFin < y.toFin
instance (x y : BitVec n) : Decidable (x < y) :=
inferInstanceAs (Decidable (x.toNat < y.toNat))
inferInstanceAs (Decidable (x.toFin < y.toFin))
/--
Unsigned less-than-or-equal-to for bit vectors.
SMT-Lib name: `bvule`.
-/
protected def ule (x y : BitVec n) : Bool := x.toNat y.toNat
protected def ule (x y : BitVec n) : Bool := x.toFin y.toFin
instance : LE (BitVec n) where le := (·.toNat ·.toNat)
instance : LE (BitVec n) where le x y := x.toFin y.toFin
instance (x y : BitVec n) : Decidable (x y) :=
inferInstanceAs (Decidable (x.toNat y.toNat))
inferInstanceAs (Decidable (x.toFin y.toFin))
/--
Signed less-than for bit vectors.
@@ -362,87 +266,6 @@ SMT-Lib name: `bvsle`.
-/
protected def sle (x y : BitVec n) : Bool := x.toInt y.toInt
end relations
section cast
/-- `cast eq i` embeds `i` into an equal `BitVec` type. -/
@[inline] def cast (eq : n = m) (i : BitVec n) : BitVec m := .ofNatLt i.toNat (eq i.isLt)
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
cast h (BitVec.ofNat n x) = BitVec.ofNat m x := by
subst h; rfl
@[simp] theorem cast_cast {n m k : Nat} (h₁ : n = m) (h₂ : m = k) (x : BitVec n) :
cast h₂ (cast h₁ x) = cast (h₁ h₂) x :=
rfl
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) : cast h x = x := rfl
/--
Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to yield a
new bitvector of size `len`. If `start + len > n`, then the vector will be zero-padded in the
high bits.
-/
def extractLsb' (start len : Nat) (a : BitVec n) : BitVec len := .ofNat _ (a.toNat >>> start)
/--
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
yield a new bitvector of size `hi - lo + 1`.
SMT-Lib name: `extract`.
-/
def extractLsb (hi lo : Nat) (a : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ a
/--
A version of `zeroExtend` that requires a proof, but is a noop.
-/
def zeroExtend' {n w : Nat} (le : n w) (x : BitVec n) : BitVec w :=
x.toNat#'(by
apply Nat.lt_of_lt_of_le x.isLt
exact Nat.pow_le_pow_of_le_right (by trivial) le)
/--
`shiftLeftZeroExtend x n` returns `zeroExtend (w+n) x <<< n` without
needing to compute `x % 2^(2+n)`.
-/
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w+m) :=
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w+m) := by
simp [Nat.shiftLeft_eq, Nat.pow_add]
apply Nat.mul_lt_mul_of_pos_right p
exact (Nat.two_pow_pos m)
(msbs.toNat <<< m)#'(shiftLeftLt msbs.isLt m)
/--
Zero extend vector `x` of length `w` by adding zeros in the high bits until it has length `v`.
If `v < w` then it truncates the high bits instead.
SMT-Lib name: `zero_extend`.
-/
def zeroExtend (v : Nat) (x : BitVec w) : BitVec v :=
if h : w v then
zeroExtend' h x
else
.ofNat v x.toNat
/--
Truncate the high bits of bitvector `x` of length `w`, resulting in a vector of length `v`.
If `v > w` then it zero-extends the vector instead.
-/
abbrev truncate := @zeroExtend
/--
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant
bit in `x`. If `x` is an empty vector, then the sign is treated as zero.
SMT-Lib name: `sign_extend`.
-/
def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt
end cast
section bitwise
/--
Bitwise AND for bit vectors.
@@ -452,8 +275,8 @@ Bitwise AND for bit vectors.
SMT-Lib name: `bvand`.
-/
protected def and (x y : BitVec n) : BitVec n :=
(x.toNat &&& y.toNat)#'(Nat.and_lt_two_pow x.toNat y.isLt)
protected def and (x y : BitVec n) : BitVec n where toFin :=
x.toNat &&& y.toNat, Nat.and_lt_two_pow x.toNat y.isLt
instance : AndOp (BitVec w) := .and
/--
@@ -465,8 +288,8 @@ Bitwise OR for bit vectors.
SMT-Lib name: `bvor`.
-/
protected def or (x y : BitVec n) : BitVec n :=
(x.toNat ||| y.toNat)#'(Nat.or_lt_two_pow x.isLt y.isLt)
protected def or (x y : BitVec n) : BitVec n where toFin :=
x.toNat ||| y.toNat, Nat.or_lt_two_pow x.isLt y.isLt
instance : OrOp (BitVec w) := .or
/--
@@ -478,8 +301,8 @@ instance : OrOp (BitVec w) := ⟨.or⟩
SMT-Lib name: `bvxor`.
-/
protected def xor (x y : BitVec n) : BitVec n :=
(x.toNat ^^^ y.toNat)#'(Nat.xor_lt_two_pow x.isLt y.isLt)
protected def xor (x y : BitVec n) : BitVec n where toFin :=
x.toNat ^^^ y.toNat, Nat.xor_lt_two_pow x.isLt y.isLt
instance : Xor (BitVec w) := .xor
/--
@@ -490,16 +313,25 @@ Bitwise NOT for bit vectors.
```
SMT-Lib name: `bvnot`.
-/
protected def not (x : BitVec n) : BitVec n := allOnes n ^^^ x
protected def not (x : BitVec n) : BitVec n :=
allOnes n ^^^ x
instance : Complement (BitVec w) := .not
/-- The `BitVec` with value `(2^n + (i mod 2^n)) mod 2^n`. -/
protected def ofInt (n : Nat) (i : Int) : BitVec n :=
match i with
| Int.ofNat a => .ofNat n a
| Int.negSucc a => ~~~.ofNat n a
instance : IntCast (BitVec w) := BitVec.ofInt w
/--
Left shift for bit vectors. The low bits are filled with zeros. As a numeric operation, this is
equivalent to `a * 2^s`, modulo `2^n`.
SMT-Lib name: `bvshl` except this operator uses a `Nat` shift value.
-/
protected def shiftLeft (a : BitVec n) (s : Nat) : BitVec n := (a.toNat <<< s)#n
protected def shiftLeft (a : BitVec n) (s : Nat) : BitVec n := .ofNat n (a.toNat <<< s)
instance : HShiftLeft (BitVec w) Nat (BitVec w) := .shiftLeft
/--
@@ -509,11 +341,11 @@ As a numeric operation, this is equivalent to `a / 2^s`, rounding down.
SMT-Lib name: `bvlshr` except this operator uses a `Nat` shift value.
-/
def ushiftRight (a : BitVec n) (s : Nat) : BitVec n :=
(a.toNat >>> s)#'(by
a.toNat >>> s, by
let a, lt := a
simp only [BitVec.toNat, Nat.shiftRight_eq_div_pow, Nat.div_lt_iff_lt_mul (Nat.two_pow_pos s)]
rw [Nat.mul_one a]
exact Nat.mul_lt_mul_of_lt_of_le' lt (Nat.two_pow_pos s) (Nat.le_refl 1))
exact Nat.mul_lt_mul_of_lt_of_le' lt (Nat.two_pow_pos s) (Nat.le_refl 1)
instance : HShiftRight (BitVec w) Nat (BitVec w) := .ushiftRight
@@ -551,6 +383,25 @@ SMT-Lib name: `rotate_right` except this operator uses a `Nat` shift amount.
-/
def rotateRight (x : BitVec w) (n : Nat) : BitVec w := x >>> n ||| x <<< (w - n)
/--
A version of `zeroExtend` that requires a proof, but is a noop.
-/
def zeroExtend' {n w : Nat} (le : n w) (x : BitVec n) : BitVec w :=
x.toNat, by
apply Nat.lt_of_lt_of_le x.isLt
exact Nat.pow_le_pow_of_le_right (by trivial) le
/--
`shiftLeftZeroExtend x n` returns `zeroExtend (w+n) x <<< n` without
needing to compute `x % 2^(2+n)`.
-/
def shiftLeftZeroExtend (msbs : BitVec w) (m : Nat) : BitVec (w+m) :=
let shiftLeftLt {x : Nat} (p : x < 2^w) (m : Nat) : x <<< m < 2^(w+m) := by
simp [Nat.shiftLeft_eq, Nat.pow_add]
apply Nat.mul_lt_mul_of_pos_right p
exact (Nat.two_pow_pos m)
msbs.toNat <<< m, shiftLeftLt msbs.isLt m
/--
Concatenation of bitvectors. This uses the "big endian" convention that the more significant
input is on the left, so `0xAB#8 ++ 0xCD#8 = 0xABCD#16`.
@@ -562,6 +413,21 @@ def append (msbs : BitVec n) (lsbs : BitVec m) : BitVec (n+m) :=
instance : HAppend (BitVec w) (BitVec v) (BitVec (w + v)) := .append
/--
Extraction of bits `start` to `start + len - 1` from a bit vector of size `n` to yield a
new bitvector of size `len`. If `start + len > n`, then the vector will be zero-padded in the
high bits.
-/
def extractLsb' (start len : Nat) (a : BitVec n) : BitVec len := .ofNat _ (a.toNat >>> start)
/--
Extraction of bits `hi` (inclusive) down to `lo` (inclusive) from a bit vector of size `n` to
yield a new bitvector of size `hi - lo + 1`.
SMT-Lib name: `extract`.
-/
def extractLsb (hi lo : Nat) (a : BitVec n) : BitVec (hi - lo + 1) := extractLsb' lo _ a
-- TODO: write this using multiplication
/-- `replicate i x` concatenates `i` copies of `x` into a new vector of length `w*i`. -/
def replicate : (i : Nat) BitVec w BitVec (w*i)
@@ -571,6 +437,70 @@ def replicate : (i : Nat) → BitVec w → BitVec (w*i)
rw [Nat.mul_add, Nat.add_comm, Nat.mul_one]
hEq (x ++ replicate n x)
/-- Fills a bitvector with `w` copies of the bit `b`. -/
def fill (w : Nat) (b : Bool) : BitVec w := bif b then -1 else 0
/--
Zero extend vector `x` of length `w` by adding zeros in the high bits until it has length `v`.
If `v < w` then it truncates the high bits instead.
SMT-Lib name: `zero_extend`.
-/
def zeroExtend (v : Nat) (x : BitVec w) : BitVec v :=
if h : w v then
zeroExtend' h x
else
.ofNat v x.toNat
/--
Truncate the high bits of bitvector `x` of length `w`, resulting in a vector of length `v`.
If `v > w` then it zero-extends the vector instead.
-/
abbrev truncate := @zeroExtend
/--
Sign extend a vector of length `w`, extending with `i` additional copies of the most significant
bit in `x`. If `x` is an empty vector, then the sign is treated as zero.
SMT-Lib name: `sign_extend`.
-/
def signExtend (v : Nat) (x : BitVec w) : BitVec v := .ofInt v x.toInt
/-! We add simp-lemmas that rewrite bitvector operations into the equivalent notation -/
@[simp] theorem append_eq (x : BitVec w) (y : BitVec v) : BitVec.append x y = x ++ y := rfl
@[simp] theorem shiftLeft_eq (x : BitVec w) (n : Nat) : BitVec.shiftLeft x n = x <<< n := rfl
@[simp] theorem ushiftRight_eq (x : BitVec w) (n : Nat) : BitVec.ushiftRight x n = x >>> n := rfl
@[simp] theorem not_eq (x : BitVec w) : BitVec.not x = ~~~x := rfl
@[simp] theorem and_eq (x y : BitVec w) : BitVec.and x y = x &&& y := rfl
@[simp] theorem or_eq (x y : BitVec w) : BitVec.or x y = x ||| y := rfl
@[simp] theorem xor_eq (x y : BitVec w) : BitVec.xor x y = x ^^^ y := rfl
@[simp] theorem neg_eq (x : BitVec w) : BitVec.neg x = -x := rfl
@[simp] theorem add_eq (x y : BitVec w) : BitVec.add x y = x + y := rfl
@[simp] theorem sub_eq (x y : BitVec w) : BitVec.sub x y = x - y := rfl
@[simp] theorem mul_eq (x y : BitVec w) : BitVec.mul x y = x * y := rfl
@[simp] theorem zero_eq : BitVec.zero n = 0#n := rfl
@[simp] theorem cast_ofNat {n m : Nat} (h : n = m) (x : Nat) :
cast h (BitVec.ofNat n x) = BitVec.ofNat m x := by
subst h; rfl
@[simp] theorem cast_cast {n m k : Nat} (h₁ : n = m) (h₂ : m = k) (x : BitVec n) :
cast h₂ (cast h₁ x) = cast (h₁ h₂) x :=
rfl
@[simp] theorem cast_eq {n : Nat} (h : n = n) (x : BitVec n) :
cast h x = x :=
rfl
/-- Turn a `Bool` into a bitvector of length `1` -/
def ofBool (b : Bool) : BitVec 1 := cond b 1 0
@[simp] theorem ofBool_false : ofBool false = 0 := by trivial
@[simp] theorem ofBool_true : ofBool true = 1 := by trivial
/-- The empty bitvector -/
abbrev nil : BitVec 0 := 0
/-!
### Cons and Concat
We give special names to the operations of adding a single bit to either end of a bitvector.
@@ -588,6 +518,14 @@ def concat {n} (msbs : BitVec n) (lsb : Bool) : BitVec (n+1) := msbs ++ (ofBool
def cons {n} (msb : Bool) (lsbs : BitVec n) : BitVec (n+1) :=
((ofBool msb) ++ lsbs).cast (Nat.add_comm ..)
/-- All empty bitvectors are equal -/
instance : Subsingleton (BitVec 0) where
allEq := by intro 0, _ 0, _; rfl
/-- Every bitvector of length 0 is equal to `nil`, i.e., there is only one empty bitvector -/
theorem eq_nil : (x : BitVec 0), x = nil
| ofFin 0, _ => rfl
theorem append_ofBool (msbs : BitVec w) (lsb : Bool) :
msbs ++ ofBool lsb = concat msbs lsb :=
rfl
@@ -595,23 +533,3 @@ theorem append_ofBool (msbs : BitVec w) (lsb : Bool) :
theorem ofBool_append (msb : Bool) (lsbs : BitVec w) :
ofBool msb ++ lsbs = (cons msb lsbs).cast (Nat.add_comm ..) :=
rfl
end bitwise
section normalization_eqs
/-! We add simp-lemmas that rewrite bitvector operations into the equivalent notation -/
@[simp] theorem append_eq (x : BitVec w) (y : BitVec v) : BitVec.append x y = x ++ y := rfl
@[simp] theorem shiftLeft_eq (x : BitVec w) (n : Nat) : BitVec.shiftLeft x n = x <<< n := rfl
@[simp] theorem ushiftRight_eq (x : BitVec w) (n : Nat) : BitVec.ushiftRight x n = x >>> n := rfl
@[simp] theorem not_eq (x : BitVec w) : BitVec.not x = ~~~x := rfl
@[simp] theorem and_eq (x y : BitVec w) : BitVec.and x y = x &&& y := rfl
@[simp] theorem or_eq (x y : BitVec w) : BitVec.or x y = x ||| y := rfl
@[simp] theorem xor_eq (x y : BitVec w) : BitVec.xor x y = x ^^^ y := rfl
@[simp] theorem neg_eq (x : BitVec w) : BitVec.neg x = -x := rfl
@[simp] theorem add_eq (x y : BitVec w) : BitVec.add x y = x + y := rfl
@[simp] theorem sub_eq (x y : BitVec w) : BitVec.sub x y = x - y := rfl
@[simp] theorem mul_eq (x y : BitVec w) : BitVec.mul x y = x * y := rfl
@[simp] theorem zero_eq : BitVec.zero n = 0#n := rfl
end normalization_eqs
end BitVec

View File

@@ -1,5 +1,6 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Copyright (c) 2023 by the authors listed in the file AUTHORS and their
institutional affiliations. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Harun Khan, Abdalrhman M Mohamed, Joe Hendrix
-/
@@ -29,23 +30,9 @@ https://github.com/mhk119/lean-smt/blob/bitvec/Smt/Data/Bitwise.lean.
open Nat Bool
namespace Bool
/-- At least two out of three booleans are true. -/
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c
@[simp] theorem atLeastTwo_false_left : atLeastTwo false b c = (b && c) := by simp [atLeastTwo]
@[simp] theorem atLeastTwo_false_mid : atLeastTwo a false c = (a && c) := by simp [atLeastTwo]
@[simp] theorem atLeastTwo_false_right : atLeastTwo a b false = (a && b) := by simp [atLeastTwo]
@[simp] theorem atLeastTwo_true_left : atLeastTwo true b c = (b || c) := by cases b <;> cases c <;> simp [atLeastTwo]
@[simp] theorem atLeastTwo_true_mid : atLeastTwo a true c = (a || c) := by cases a <;> cases c <;> simp [atLeastTwo]
@[simp] theorem atLeastTwo_true_right : atLeastTwo a b true = (a || b) := by cases a <;> cases b <;> simp [atLeastTwo]
end Bool
/-! ### Preliminaries -/
namespace BitVec
namespace Std.BitVec
private theorem testBit_limit {x i : Nat} (x_lt_succ : x < 2^(i+1)) :
testBit x i = decide (x 2^i) := by
@@ -89,29 +76,18 @@ private theorem mod_two_pow_succ (x i : Nat) :
have not_j_ge_i : ¬(j i) := Nat.not_le_of_lt j_lt_i
simp [j_lt_i, j_le_i, not_j_ge_i, j_le_i_succ]
private theorem mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ
(x y i : Nat) (c : Bool) : x % 2^i + (y % 2^i + c.toNat) < 2^(i+1) := by
have : c.toNat 1 := Bool.toNat_le c
rw [Nat.pow_succ]
omega
private theorem mod_two_pow_lt (x i : Nat) : x % 2 ^ i < 2^i := Nat.mod_lt _ (Nat.two_pow_pos _)
/-! ### Addition -/
/-- carry i x y c returns true if the `i` carry bit is true when computing `x + y + c`. -/
def carry (i : Nat) (x y : BitVec w) (c : Bool) : Bool :=
decide (x.toNat % 2^i + y.toNat % 2^i + c.toNat 2^i)
/-- carry w x y c returns true if the `w` carry bit is true when computing `x + y + c`. -/
def carry (w x y : Nat) (c : Bool) : Bool := decide (x % 2^w + y % 2^w + c.toNat 2^w)
@[simp] theorem carry_zero : carry 0 x y c = c := by
cases c <;> simp [carry, mod_one]
theorem carry_succ (i : Nat) (x y : BitVec w) (c : Bool) :
carry (i+1) x y c = atLeastTwo (x.getLsb i) (y.getLsb i) (carry i x y c) := by
simp only [carry, mod_two_pow_succ, atLeastTwo, getLsb]
simp only [Nat.pow_succ']
have sum_bnd : x.toNat%2^i + (y.toNat%2^i + c.toNat) < 2*2^i := by
simp only [ Nat.pow_succ']
exact mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ ..
cases x.toNat.testBit i <;> cases y.toNat.testBit i <;> (simp; omega)
/-- At least two out of three booleans are true. -/
abbrev atLeastTwo (a b c : Bool) : Bool := a && b || a && c || b && c
/-- Carry function for bitwise addition. -/
def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, Bool.xor x (Bool.xor y c))
@@ -120,9 +96,25 @@ def adcb (x y c : Bool) : Bool × Bool := (atLeastTwo x y c, Bool.xor x (Bool.xo
def adc (x y : BitVec w) : Bool Bool × BitVec w :=
iunfoldr fun (i : Fin w) c => adcb (x.getLsb i) (y.getLsb i) c
theorem adc_overflow_limit (x y i : Nat) (c : Bool) : x % 2^i + (y % 2^i + c.toNat) < 2^(i+1) := by
have : c.toNat 1 := Bool.toNat_le_one c
rw [Nat.pow_succ]
omega
theorem carry_succ (w x y : Nat) (c : Bool) :
carry (succ w) x y c = atLeastTwo (x.testBit w) (y.testBit w) (carry w x y c) := by
simp only [carry, mod_two_pow_succ, atLeastTwo]
simp only [Nat.pow_succ']
generalize testBit x w = xh
generalize testBit y w = yh
have sum_bnd : x%2^w + (y%2^w + c.toNat) < 2*2^w := by
simp only [ Nat.pow_succ']
exact adc_overflow_limit x y w c
cases xh <;> cases yh <;> (simp; omega)
theorem getLsb_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool) :
getLsb (x + y + zeroExtend w (ofBool c)) i =
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y c)) := by
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x.toNat y.toNat c)) := by
let x, x_lt := x
let y, y_lt := y
simp only [getLsb, toNat_add, toNat_zeroExtend, i_lt, toNat_ofFin, toNat_ofBool,
@@ -137,27 +129,33 @@ theorem getLsb_add_add_bool {i : Nat} (i_lt : i < w) (x y : BitVec w) (c : Bool)
Bool.true_and,
Nat.add_assoc,
Nat.add_left_comm (_%_) (_ * _) _,
testBit_limit (mod_two_pow_add_mod_two_pow_add_bool_lt_two_pow_succ x y i c)
testBit_limit (adc_overflow_limit x y i c)
]
simp [testBit_to_div_mod, carry, Nat.add_assoc]
theorem getLsb_add {i : Nat} (i_lt : i < w) (x y : BitVec w) :
getLsb (x + y) i =
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x y false)) := by
Bool.xor (getLsb x i) (Bool.xor (getLsb y i) (carry i x.toNat y.toNat false)) := by
simpa using getLsb_add_add_bool i_lt x y false
theorem adc_spec (x y : BitVec w) (c : Bool) :
adc x y c = (carry w x y c, x + y + zeroExtend w (ofBool c)) := by
adc x y c = (carry w x.toNat y.toNat c, x + y + zeroExtend w (ofBool c)) := by
simp only [adc]
apply iunfoldr_replace
(fun i => carry i x y c)
(fun i => carry i x.toNat y.toNat c)
(x + y + zeroExtend w (ofBool c))
c
case init =>
simp [carry, Nat.mod_one]
cases c <;> rfl
case step =>
simp [adcb, Prod.mk.injEq, carry_succ, getLsb_add_add_bool]
intro i, lt
simp only [adcb, Prod.mk.injEq, carry_succ]
apply And.intro
case left =>
rw [testBit_toNat, testBit_toNat]
case right =>
simp [getLsb_add_add_bool lt]
theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := by
simp [adc_spec]
@@ -173,5 +171,3 @@ theorem add_eq_adc (w : Nat) (x y : BitVec w) : x + y = (adc x y false).snd := b
/-- Subtracting `x` from the all ones bitvector is equivalent to taking its complement -/
theorem allOnes_sub_eq_not (x : BitVec w) : allOnes w - x = ~~~x := by
rw [ add_not_self x, BitVec.add_comm, add_sub_cancel]
end BitVec

View File

@@ -8,7 +8,7 @@ import Init.Data.BitVec.Lemmas
import Init.Data.Nat.Lemmas
import Init.Data.Fin.Iterate
namespace BitVec
namespace Std.BitVec
/--
iunfoldr is an iterative operation that applies a function `f` repeatedly.
@@ -57,5 +57,3 @@ theorem iunfoldr_replace
(step : (i : Fin w), f i (state i.val) = (state (i.val+1), value.getLsb i.val)) :
iunfoldr f a = (state w, value) := by
simp [iunfoldr.eq_test state value a init step]
end BitVec

View File

@@ -9,7 +9,7 @@ import Init.Data.BitVec.Basic
import Init.Data.Fin.Lemmas
import Init.Data.Nat.Lemmas
namespace BitVec
namespace Std.BitVec
/--
This normalized a bitvec using `ofFin` to `ofNat`.
@@ -23,12 +23,9 @@ theorem eq_of_toNat_eq {n} : ∀ {i j : BitVec n}, i.toNat = j.toNat → i = j
@[simp] theorem val_toFin (x : BitVec w) : x.toFin.val = x.toNat := rfl
@[bv_toNat] theorem toNat_eq (x y : BitVec n) : x = y x.toNat = y.toNat :=
theorem toNat_eq (x y : BitVec n) : x = y x.toNat = y.toNat :=
Iff.intro (congrArg BitVec.toNat) eq_of_toNat_eq
@[bv_toNat] theorem toNat_ne (x y : BitVec n) : x y x.toNat y.toNat := by
rw [Ne, toNat_eq]
theorem toNat_lt (x : BitVec n) : x.toNat < 2^n := x.toFin.2
theorem testBit_toNat (x : BitVec w) : x.toNat.testBit i = x.getLsb i := rfl
@@ -81,8 +78,6 @@ theorem eq_of_getMsb_eq {x y : BitVec w}
have q := pred w - 1 - i, q_lt
simpa [q_lt, Nat.sub_sub_self, r] using q
@[simp] theorem of_length_zero {x : BitVec 0} : x = 0#0 := by ext; simp
theorem eq_of_toFin_eq : {x y : BitVec w}, x.toFin = y.toFin x = y
| _, _, _, _, rfl => rfl
@@ -95,15 +90,9 @@ theorem ofNat_one (n : Nat) : BitVec.ofNat 1 n = BitVec.ofBool (n % 2 = 1) := b
theorem ofBool_eq_iff_eq : (b b' : Bool), BitVec.ofBool b = BitVec.ofBool b' b = b' := by
decide
@[simp, bv_toNat] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val := rfl
@[simp] theorem toNat_ofFin (x : Fin (2^n)) : (BitVec.ofFin x).toNat = x.val := rfl
@[simp] theorem toNat_ofNatLt (x : Nat) (p : x < 2^w) : (x#'p).toNat = x := rfl
@[simp] theorem getLsb_ofNatLt {n : Nat} (x : Nat) (lt : x < 2^n) (i : Nat) :
getLsb (x#'lt) i = x.testBit i := by
simp [getLsb, BitVec.ofNatLt]
@[simp, bv_toNat] theorem toNat_ofNat (x w : Nat) : (x#w).toNat = x % 2^w := by
@[simp] theorem toNat_ofNat (x w : Nat) : (x#w).toNat = x % 2^w := by
simp [BitVec.toNat, BitVec.ofNat, Fin.ofNat']
-- Remark: we don't use `[simp]` here because simproc` subsumes it for literals.
@@ -112,9 +101,7 @@ theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
getLsb (x#n) i = (i < n && x.testBit i) := by
simp [getLsb, BitVec.ofNat, Fin.val_ofNat']
@[simp, deprecated toNat_ofNat] theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
@[simp] theorem getLsb_zero : (0#w).getLsb i = false := by simp [getLsb]
@[deprecated toNat_ofNat] theorem toNat_zero (n : Nat) : (0#n).toNat = 0 := by trivial
@[simp] theorem toNat_mod_cancel (x : BitVec n) : x.toNat % (2^n) = x.toNat :=
Nat.mod_eq_of_lt x.isLt
@@ -122,49 +109,35 @@ theorem getLsb_ofNat (n : Nat) (x : Nat) (i : Nat) :
private theorem lt_two_pow_of_le {x m n : Nat} (lt : x < 2 ^ m) (le : m n) : x < 2 ^ n :=
Nat.lt_of_lt_of_le lt (Nat.pow_le_pow_of_le_right (by trivial : 0 < 2) le)
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : x.toNat#m = truncate m x := by
let x, lt_n := x
unfold truncate
unfold zeroExtend
if h : n m then
unfold zeroExtend'
have lt_m : x < 2 ^ m := lt_two_pow_of_le lt_n h
simp [h, lt_m, Nat.mod_eq_of_lt, BitVec.toNat, BitVec.ofNat, Fin.ofNat']
else
simp [h]
/-! ### msb -/
@[simp] theorem msb_zero : (0#w).msb = false := by simp [BitVec.msb, getMsb]
theorem msb_eq_getLsb_last (x : BitVec w) :
x.msb = x.getLsb (w - 1) := by
simp [BitVec.msb, getMsb, getLsb]
rcases w with rfl | w
· simp [BitVec.eq_nil x]
· simp
@[bv_toNat] theorem getLsb_last (x : BitVec w) :
x.getLsb (w-1) = decide (2 ^ (w-1) x.toNat) := by
rcases w with rfl | w
· simp
· simp only [Nat.zero_lt_succ, decide_True, getLsb, Nat.testBit, Nat.succ_sub_succ_eq_sub,
theorem msb_eq_decide (x : BitVec (Nat.succ w)) : BitVec.msb x = decide (2 ^ w x.toNat) := by
simp only [BitVec.msb, getMsb, Nat.zero_lt_succ,
decide_True, getLsb, Nat.testBit, Nat.succ_sub_succ_eq_sub,
Nat.sub_zero, Nat.and_one_is_mod, Bool.true_and, Nat.shiftRight_eq_div_pow]
rcases (Nat.lt_or_ge (BitVec.toNat x) (2 ^ w)) with h | h
· simp [Nat.div_eq_of_lt h, h]
· simp only [h]
rw [Nat.div_eq_sub_div (Nat.two_pow_pos w) h, Nat.div_eq_of_lt]
· decide
· have : BitVec.toNat x < 2^w + 2^w := by simpa [Nat.pow_succ, Nat.mul_two] using x.isLt
omega
@[bv_toNat] theorem getLsb_succ_last (x : BitVec (w + 1)) :
x.getLsb w = decide (2 ^ w x.toNat) := getLsb_last x
@[bv_toNat] theorem msb_eq_decide (x : BitVec w) : BitVec.msb x = decide (2 ^ (w-1) x.toNat) := by
simp [msb_eq_getLsb_last, getLsb_last]
theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat 2^(n-1) := by
match n with
| 0 =>
simp [BitVec.msb, BitVec.getMsb] at p
| n + 1 =>
simp [BitVec.msb_eq_decide] at p
simp only [Nat.add_sub_cancel]
exact p
rcases (Nat.lt_or_ge (BitVec.toNat x) (2 ^ w)) with h | h
· simp [Nat.div_eq_of_lt h, h]
· simp only [h]
rw [Nat.div_eq_sub_div (Nat.two_pow_pos w) h, Nat.div_eq_of_lt]
· decide
· have : BitVec.toNat x < 2^w + 2^w := by simpa [Nat.pow_succ, Nat.mul_two] using x.isLt
omega
/-! ### cast -/
@[simp, bv_toNat] theorem toNat_cast (h : w = v) (x : BitVec w) : (cast h x).toNat = x.toNat := rfl
@[simp] theorem toNat_cast (h : w = v) (x : BitVec w) : (cast h x).toNat = x.toNat := rfl
@[simp] theorem toFin_cast (h : w = v) (x : BitVec w) :
(cast h x).toFin = x.toFin.cast (by rw [h]) :=
rfl
@@ -177,61 +150,14 @@ theorem toNat_ge_of_msb_true {x : BitVec n} (p : BitVec.msb x = true) : x.toNat
@[simp] theorem msb_cast (h : w = v) (x : BitVec w) : (cast h x).msb = x.msb := by
simp [BitVec.msb]
/-! ### toInt/ofInt -/
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem toInt_eq_toNat_cond (i : BitVec n) :
i.toInt =
if 2*i.toNat < 2^n then
(i.toNat : Int)
else
(i.toNat : Int) - (2^n : Nat) := by
unfold BitVec.toInt
split <;> omega
theorem toInt_eq_toNat_bmod (x : BitVec n) : x.toInt = Int.bmod x.toNat (2^n) := by
simp only [toInt_eq_toNat_cond]
split
case inl g =>
rw [Int.bmod_pos] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
omega
case inr g =>
rw [Int.bmod_neg] <;> simp only [Int.ofNat_emod, toNat_mod_cancel]
omega
/-- Prove equality of bitvectors in terms of nat operations. -/
theorem eq_of_toInt_eq {i j : BitVec n} : i.toInt = j.toInt i = j := by
intro eq
simp [toInt_eq_toNat_cond] at eq
apply eq_of_toNat_eq
revert eq
have _ilt := i.isLt
have _jlt := j.isLt
split <;> split <;> omega
@[simp] theorem toNat_ofInt {n : Nat} (i : Int) :
(BitVec.ofInt n i).toNat = (i % (2^n : Nat)).toNat := by
unfold BitVec.ofInt
simp
theorem toInt_ofNat {n : Nat} (x : Nat) :
(BitVec.ofNat n x).toInt = (x : Int).bmod (2^n) := by
simp [toInt_eq_toNat_bmod]
@[simp] theorem toInt_ofInt {n : Nat} (i : Int) :
(BitVec.ofInt n i).toInt = i.bmod (2^n) := by
have _ := Nat.two_pow_pos n
have p : 0 i % (2^n : Nat) := by omega
simp [toInt_eq_toNat_bmod, Int.toNat_of_nonneg p]
/-! ### zeroExtend and truncate -/
@[simp, bv_toNat] theorem toNat_zeroExtend' {m n : Nat} (p : m n) (x : BitVec m) :
@[simp] theorem toNat_zeroExtend' {m n : Nat} (p : m n) (x : BitVec m) :
(zeroExtend' p x).toNat = x.toNat := by
unfold zeroExtend'
simp [p, x.isLt, Nat.mod_eq_of_lt]
@[bv_toNat] theorem toNat_zeroExtend (i : Nat) (x : BitVec n) :
theorem toNat_zeroExtend (i : Nat) (x : BitVec n) :
BitVec.toNat (zeroExtend i x) = x.toNat % 2^i := by
let x, lt_n := x
simp only [zeroExtend]
@@ -241,9 +167,6 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
else
simp [n_le_i, toNat_ofNat]
@[simp, bv_toNat] theorem toNat_truncate (x : BitVec n) : (truncate i x).toNat = x.toNat % 2^i :=
toNat_zeroExtend i x
@[simp] theorem zeroExtend_eq (x : BitVec n) : zeroExtend n x = x := by
apply eq_of_toNat_eq
let x, lt_n := x
@@ -255,27 +178,8 @@ theorem toInt_ofNat {n : Nat} (x : Nat) :
@[simp] theorem truncate_eq (x : BitVec n) : truncate n x = x := zeroExtend_eq x
@[simp] theorem ofNat_toNat (m : Nat) (x : BitVec n) : x.toNat#m = truncate m x := by
apply eq_of_toNat_eq
simp
/-- Moves one-sided left toNat equality to BitVec equality. -/
theorem toNat_eq_nat (x : BitVec w) (y : Nat)
: (x.toNat = y) (y < 2^w (x = y#w)) := by
apply Iff.intro
· intro eq
simp at eq
have lt := x.isLt
simp [eq] at lt
simp [eq, lt, x.isLt]
· intro eq
simp [Nat.mod_eq_of_lt, eq]
/-- Moves one-sided right toNat equality to BitVec equality. -/
theorem nat_eq_toNat (x : BitVec w) (y : Nat)
: (y = x.toNat) (y < 2^w (x = y#w)) := by
rw [@eq_comm _ _ x.toNat]
apply toNat_eq_nat
@[simp] theorem toNat_truncate (x : BitVec n) : (truncate i x).toNat = x.toNat % 2^i :=
toNat_zeroExtend i x
@[simp] theorem getLsb_zeroExtend' (ge : m n) (x : BitVec n) (i : Nat) :
getLsb (zeroExtend' ge x) i = getLsb x i := by
@@ -289,23 +193,6 @@ theorem nat_eq_toNat (x : BitVec w) (y : Nat)
getLsb (truncate m x) i = (decide (i < m) && getLsb x i) :=
getLsb_zeroExtend m x i
@[simp] theorem zeroExtend_zeroExtend_of_le (x : BitVec w) (h : k l) :
(x.zeroExtend l).zeroExtend k = x.zeroExtend k := by
ext i
simp only [getLsb_zeroExtend, Fin.is_lt, decide_True, Bool.true_and]
have p := lt_of_getLsb x i
revert p
cases getLsb x i <;> simp; omega
@[simp] theorem truncate_truncate_of_le (x : BitVec w) (h : k l) :
(x.truncate l).truncate k = x.truncate k :=
zeroExtend_zeroExtend_of_le x h
theorem msb_zeroExtend (x : BitVec w) : (x.zeroExtend v).msb = (decide (0 < v) && x.getLsb (v - 1)) := by
rw [msb_eq_getLsb_last]
simp only [getLsb_zeroExtend]
cases getLsb x (v - 1) <;> simp; omega
/-! ## extractLsb -/
@[simp]
@@ -332,12 +219,31 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
/-! ### allOnes -/
private theorem allOnes_def :
allOnes v = .ofFin (0, Nat.two_pow_pos v - 1 % 2^v, Nat.mod_lt _ (Nat.two_pow_pos v)) := by
rfl
@[simp] theorem toNat_allOnes : (allOnes v).toNat = 2^v - 1 := by
unfold allOnes
simp
simp only [allOnes_def, toNat_ofFin, Fin.coe_sub, Nat.zero_add]
by_cases h : v = 0
· subst h
rfl
· rw [Nat.mod_eq_of_lt (Nat.one_lt_two_pow h), Nat.mod_eq_of_lt]
exact Nat.pred_lt_self (Nat.two_pow_pos v)
@[simp] theorem getLsb_allOnes : (allOnes v).getLsb i = decide (i < v) := by
simp [allOnes]
simp only [allOnes_def, getLsb_ofFin, Fin.coe_sub, Nat.zero_add, Nat.testBit_mod_two_pow]
if h : i < v then
simp only [h, decide_True, Bool.true_and]
match i, v, h with
| i, (v + 1), h =>
rw [Nat.mod_eq_of_lt (by simp), Nat.testBit_two_pow_sub_one]
simp [h]
else
simp [h]
@[simp] theorem negOne_eq_allOnes : -1#w = allOnes w :=
rfl
/-! ### or -/
@@ -346,9 +252,10 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
@[simp] theorem toFin_or (x y : BitVec v) :
BitVec.toFin (x ||| y) = BitVec.toFin x ||| BitVec.toFin y := by
apply Fin.eq_of_val_eq
simp only [HOr.hOr, OrOp.or, BitVec.or, Fin.lor, val_toFin, Fin.mk.injEq]
exact (Nat.mod_eq_of_lt <| Nat.or_lt_two_pow x.isLt y.isLt).symm
@[simp] theorem getLsb_or {x y : BitVec v} : (x ||| y).getLsb i = (x.getLsb i || y.getLsb i) := by
rw [ testBit_toNat, getLsb, getLsb]
simp
@@ -360,7 +267,7 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
@[simp] theorem toFin_and (x y : BitVec v) :
BitVec.toFin (x &&& y) = BitVec.toFin x &&& BitVec.toFin y := by
apply Fin.eq_of_val_eq
simp only [HAnd.hAnd, AndOp.and, BitVec.and, Fin.land, val_toFin, Fin.mk.injEq]
exact (Nat.mod_eq_of_lt <| Nat.and_lt_two_pow _ y.isLt).symm
@[simp] theorem getLsb_and {x y : BitVec v} : (x &&& y).getLsb i = (x.getLsb i && y.getLsb i) := by
@@ -374,7 +281,7 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
@[simp] theorem toFin_xor (x y : BitVec v) :
BitVec.toFin (x ^^^ y) = BitVec.toFin x ^^^ BitVec.toFin y := by
apply Fin.eq_of_val_eq
simp only [HXor.hXor, Xor.xor, BitVec.xor, Fin.xor, val_toFin, Fin.mk.injEq]
exact (Nat.mod_eq_of_lt <| Nat.xor_lt_two_pow x.isLt y.isLt).symm
@[simp] theorem getLsb_xor {x y : BitVec v} :
@@ -386,7 +293,7 @@ protected theorem extractLsb_ofNat (x n : Nat) (hi lo : Nat) :
theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
@[simp, bv_toNat] theorem toNat_not {x : BitVec v} : (~~~x).toNat = 2^v - 1 - x.toNat := by
@[simp] theorem toNat_not {x : BitVec v} : (~~~x).toNat = 2^v - 1 - x.toNat := by
rw [Nat.sub_sub, Nat.add_comm, not_def, toNat_xor]
apply Nat.eq_of_testBit_eq
intro i
@@ -416,7 +323,7 @@ theorem not_def {x : BitVec v} : ~~~x = allOnes v ^^^ x := rfl
/-! ### shiftLeft -/
@[simp, bv_toNat] theorem toNat_shiftLeft {x : BitVec v} :
@[simp] theorem toNat_shiftLeft {x : BitVec v} :
BitVec.toNat (x <<< n) = BitVec.toNat x <<< n % 2^v :=
BitVec.toNat_ofNat _ _
@@ -452,7 +359,7 @@ theorem shiftLeftZeroExtend_eq {x : BitVec w} :
/-! ### ushiftRight -/
@[simp, bv_toNat] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
@[simp] theorem toNat_ushiftRight (x : BitVec n) (i : Nat) :
(x >>> i).toNat = x.toNat >>> i := rfl
@[simp] theorem getLsb_ushiftRight (x : BitVec n) (i j : Nat) :
@@ -522,30 +429,6 @@ theorem truncate_succ (x : BitVec w) :
have j_lt : j.val < i := Nat.lt_of_le_of_ne (Nat.le_of_succ_le_succ j.isLt) j_eq
simp [j_eq, j_lt]
/-! ### concat -/
@[simp] theorem toNat_concat (x : BitVec w) (b : Bool) :
(concat x b).toNat = x.toNat * 2 + b.toNat := by
apply Nat.eq_of_testBit_eq
simp only [concat, toNat_append, Nat.shiftLeft_eq, Nat.pow_one, toNat_ofBool, Nat.testBit_or]
cases b
· simp
· rintro (_ | i)
<;> simp [Nat.add_mod, Nat.add_comm, Nat.add_mul_div_right]
theorem getLsb_concat (x : BitVec w) (b : Bool) (i : Nat) :
(concat x b).getLsb i = if i = 0 then b else x.getLsb (i - 1) := by
simp only [concat, getLsb, toNat_append, toNat_ofBool, Nat.testBit_or, Nat.shiftLeft_eq]
cases i
· simp [Nat.mod_eq_of_lt b.toNat_lt]
· simp [Nat.div_eq_of_lt b.toNat_lt]
@[simp] theorem getLsb_concat_zero : (concat x b).getLsb 0 = b := by
simp [getLsb_concat]
@[simp] theorem getLsb_concat_succ : (concat x b).getLsb (i + 1) = x.getLsb i := by
simp [getLsb_concat]
/-! ### add -/
theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := rfl
@@ -553,7 +436,7 @@ theorem add_def {n} (x y : BitVec n) : x + y = .ofNat n (x.toNat + y.toNat) := r
/--
Definition of bitvector addition as a nat.
-/
@[simp, bv_toNat] theorem toNat_add (x y : BitVec w) : (x + y).toNat = (x.toNat + y.toNat) % 2^w := rfl
@[simp] theorem toNat_add (x y : BitVec w) : (x + y).toNat = (x.toNat + y.toNat) % 2^w := rfl
@[simp] theorem toFin_add (x y : BitVec w) : (x + y).toFin = toFin x + toFin y := rfl
@[simp] theorem ofFin_add (x : Fin (2^n)) (y : BitVec n) :
.ofFin x + y = .ofFin (x + y.toFin) := rfl
@@ -577,7 +460,7 @@ protected theorem add_comm (x y : BitVec n) : x + y = y + x := by
theorem sub_def {n} (x y : BitVec n) : x - y = .ofNat n (x.toNat + (2^n - y.toNat)) := by rfl
@[simp, bv_toNat] theorem toNat_sub {n} (x y : BitVec n) :
@[simp] theorem toNat_sub {n} (x y : BitVec n) :
(x - y).toNat = ((x.toNat + (2^n - y.toNat)) % 2^n) := rfl
@[simp] theorem toFin_sub (x y : BitVec n) : (x - y).toFin = toFin x - toFin y := rfl
@@ -599,7 +482,7 @@ theorem ofNat_sub_ofNat {n} (x y : Nat) : x#n - y#n = .ofNat n (x + (2^n - y % 2
· simp
· exact Nat.le_of_lt x.isLt
@[simp, bv_toNat] theorem toNat_neg (x : BitVec n) : (- x).toNat = (2^n - x.toNat) % 2^n := by
@[simp] theorem toNat_neg (x : BitVec n) : (- x).toNat = (2^n - x.toNat) % 2^n := by
simp [Neg.neg, BitVec.neg]
theorem sub_toAdd {n} (x y : BitVec n) : x - y = x + - y := by
@@ -614,44 +497,16 @@ theorem add_sub_cancel (x y : BitVec w) : x + y - y = x := by
rw [toNat_sub, toNat_add, Nat.mod_add_mod, Nat.add_assoc, Nat.add_sub_assoc y_toNat_le,
Nat.add_sub_cancel_left, Nat.add_mod_right, toNat_mod_cancel]
theorem negOne_eq_allOnes : -1#w = allOnes w := by
apply eq_of_toNat_eq
if g : w = 0 then
simp [g]
else
have q : 1 < 2^w := by simp [g]
have r : (2^w - 1) < 2^w := by omega
simp [Nat.mod_eq_of_lt q, Nat.mod_eq_of_lt r]
/-! ### mul -/
theorem mul_def {n} {x y : BitVec n} : x * y = (ofFin <| x.toFin * y.toFin) := by rfl
@[simp, bv_toNat] theorem toNat_mul (x y : BitVec n) : (x * y).toNat = (x.toNat * y.toNat) % 2 ^ n := rfl
theorem toNat_mul (x y : BitVec n) : (x * y).toNat = (x.toNat * y.toNat) % 2 ^ n := rfl
@[simp] theorem toFin_mul (x y : BitVec n) : (x * y).toFin = (x.toFin * y.toFin) := rfl
protected theorem mul_comm (x y : BitVec w) : x * y = y * x := by
apply eq_of_toFin_eq; simpa using Fin.mul_comm ..
instance : Std.Commutative (fun (x y : BitVec w) => x * y) := BitVec.mul_comm
protected theorem mul_assoc (x y z : BitVec w) : x * y * z = x * (y * z) := by
apply eq_of_toFin_eq; simpa using Fin.mul_assoc ..
instance : Std.Associative (fun (x y : BitVec w) => x * y) := BitVec.mul_assoc
@[simp] protected theorem mul_one (x : BitVec w) : x * 1#w = x := by
cases w
· apply Subsingleton.elim
· apply eq_of_toNat_eq; simp [Nat.mod_eq_of_lt]
@[simp] protected theorem one_mul (x : BitVec w) : 1#w * x = x := by
rw [BitVec.mul_comm, BitVec.mul_one]
instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
right_id := BitVec.mul_one
/-! ### le and lt -/
@[bv_toNat] theorem le_def (x y : BitVec n) :
theorem le_def (x y : BitVec n) :
x y x.toNat y.toNat := Iff.rfl
@[simp] theorem le_ofFin (x : BitVec n) (y : Fin (2^n)) :
@@ -661,7 +516,7 @@ instance : Std.LawfulCommIdentity (fun (x y : BitVec w) => x * y) (1#w) where
@[simp] theorem ofNat_le_ofNat {n} (x y : Nat) : (x#n) (y#n) x % 2^n y % 2^n := by
simp [le_def]
@[bv_toNat] theorem lt_def (x y : BitVec n) :
theorem lt_def (x y : BitVec n) :
x < y x.toNat < y.toNat := Iff.rfl
@[simp] theorem lt_ofFin (x : BitVec n) (y : Fin (2^n)) :
@@ -677,19 +532,3 @@ protected theorem lt_of_le_ne (x y : BitVec n) (h1 : x <= y) (h2 : ¬ x = y) : x
let y, lt := y
simp
exact Nat.lt_of_le_of_ne
/- ! ### intMax -/
/-- The bitvector of width `w` that has the largest value when interpreted as an integer. -/
def intMax (w : Nat) : BitVec w := (2^w - 1)#w
theorem getLsb_intMax_eq (w : Nat) : (intMax w).getLsb i = decide (i < w) := by
simp [intMax, getLsb]
theorem toNat_intMax_eq : (intMax w).toNat = 2^w - 1 := by
have h : 2^w - 1 < 2^w := by
have pos : 2^w > 0 := Nat.pow_pos (by decide)
omega
simp [intMax, Nat.shiftLeft_eq, Nat.one_mul, natCast_eq_ofNat, toNat_ofNat, Nat.mod_eq_of_lt h]
end BitVec

View File

@@ -48,11 +48,6 @@ theorem ne_false_iff : {b : Bool} → b ≠ false ↔ b = true := by decide
theorem eq_iff_iff {a b : Bool} : a = b (a b) := by cases b <;> simp
@[simp] theorem decide_eq_true {b : Bool} : decide (b = true) = b := by cases b <;> simp
@[simp] theorem decide_eq_false {b : Bool} : decide (b = false) = !b := by cases b <;> simp
@[simp] theorem decide_true_eq {b : Bool} : decide (true = b) = b := by cases b <;> simp
@[simp] theorem decide_false_eq {b : Bool} : decide (false = b) = !b := by cases b <;> simp
/-! ### and -/
@[simp] theorem not_and_self : (x : Bool), (!x && x) = false := by decide
@@ -217,19 +212,9 @@ def toNat (b:Bool) : Nat := cond b 1 0
@[simp] theorem toNat_true : true.toNat = 1 := rfl
theorem toNat_le (c : Bool) : c.toNat 1 := by
theorem toNat_le_one (c:Bool) : c.toNat 1 := by
cases c <;> trivial
@[deprecated toNat_le] abbrev toNat_le_one := toNat_le
theorem toNat_lt (b : Bool) : b.toNat < 2 :=
Nat.lt_succ_of_le (toNat_le _)
@[simp] theorem toNat_eq_zero (b : Bool) : b.toNat = 0 b = false := by
cases b <;> simp
@[simp] theorem toNat_eq_one (b : Bool) : b.toNat = 1 b = true := by
cases b <;> simp
end Bool
/-! ### cond -/

View File

@@ -156,19 +156,6 @@ def natAdd (n) (i : Fin m) : Fin (n + m) := ⟨n + i, Nat.add_lt_add_left i.2 _
@[inline] def pred {n : Nat} (i : Fin (n + 1)) (h : i 0) : Fin n :=
subNat 1 i <| Nat.pos_of_ne_zero <| mt (Fin.eq_of_val_eq (j := 0)) h
theorem val_inj {a b : Fin n} : a.1 = b.1 a = b := Fin.eq_of_val_eq, Fin.val_eq_of_eq
theorem val_congr {n : Nat} {a b : Fin n} (h : a = b) : (a : Nat) = (b : Nat) :=
Fin.val_inj.mpr h
theorem val_le_of_le {n : Nat} {a b : Fin n} (h : a b) : (a : Nat) (b : Nat) := h
theorem val_le_of_ge {n : Nat} {a b : Fin n} (h : a b) : (b : Nat) (a : Nat) := h
theorem val_add_one_le_of_lt {n : Nat} {a b : Fin n} (h : a < b) : (a : Nat) + 1 (b : Nat) := h
theorem val_add_one_le_of_gt {n : Nat} {a b : Fin n} (h : a > b) : (b : Nat) + 1 (a : Nat) := h
end Fin
instance [GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i where

View File

@@ -1,5 +1,6 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Copyright (c) 2023 by the authors listed in the file AUTHORS and their
institutional affiliations. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix
-/

View File

@@ -36,6 +36,8 @@ theorem pos_iff_nonempty {n : Nat} : 0 < n ↔ Nonempty (Fin n) :=
@[ext] theorem ext {a b : Fin n} (h : (a : Nat) = b) : a = b := eq_of_val_eq h
theorem val_inj {a b : Fin n} : a.1 = b.1 a = b := Fin.eq_of_val_eq, Fin.val_eq_of_eq
theorem ext_iff {a b : Fin n} : a = b a.1 = b.1 := val_inj.symm
theorem val_ne_iff {a b : Fin n} : a.1 b.1 a b := not_congr val_inj
@@ -793,12 +795,6 @@ protected theorem mul_one (k : Fin (n + 1)) : k * 1 = k := by
protected theorem mul_comm (a b : Fin n) : a * b = b * a :=
ext <| by rw [mul_def, mul_def, Nat.mul_comm]
protected theorem mul_assoc (a b c : Fin n) : a * b * c = a * (b * c) := by
apply eq_of_val_eq
simp only [val_mul]
rw [ Nat.mod_eq_of_lt a.isLt, Nat.mod_eq_of_lt b.isLt, Nat.mod_eq_of_lt c.isLt]
simp only [ Nat.mul_mod, Nat.mul_assoc]
protected theorem one_mul (k : Fin (n + 1)) : (1 : Fin (n + 1)) * k = k := by
rw [Fin.mul_comm, Fin.mul_one]

View File

@@ -158,44 +158,4 @@ instance : Div Int where
instance : Mod Int where
mod := Int.emod
/-!
# `bmod` ("balanced" mod)
Balanced mod (and balanced div) are a division and modulus pair such
that `b * (Int.bdiv a b) + Int.bmod a b = a` and `b/2 ≤ Int.bmod a b <
b/2` for all `a : Int` and `b > 0`.
This is used in Omega as well as signed bitvectors.
-/
/--
Balanced modulus. This version of Integer modulus uses the
balanced rounding convention, which guarantees that
`m/2 ≤ bmod x m < m/2` for `m ≠ 0` and `bmod x m` is congruent
to `x` modulo `m`.
If `m = 0`, then `bmod x m = x`.
-/
def bmod (x : Int) (m : Nat) : Int :=
let r := x % m
if r < (m + 1) / 2 then
r
else
r - m
/--
Balanced division. This returns the unique integer so that
`b * (Int.bdiv a b) + Int.bmod a b = a`.
-/
def bdiv (x : Int) (m : Nat) : Int :=
if m = 0 then
0
else
let q := x / m
let r := x % m
if r < (m + 1) / 2 then
q
else
q + 1
end Int

View File

@@ -22,7 +22,7 @@ namespace Int
/-! ### `/` -/
@[simp, norm_cast] theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
@[simp] theorem ofNat_ediv (m n : Nat) : ((m / n) : Int) = m / n := rfl
@[simp] theorem zero_ediv : b : Int, 0 / b = 0
| ofNat _ => show ofNat _ = _ by simp
@@ -102,7 +102,7 @@ theorem ofNat_mod (m n : Nat) : (↑(m % n) : Int) = mod m n := rfl
theorem ofNat_mod_ofNat (m n : Nat) : (m % n : Int) = (m % n) := rfl
@[simp, norm_cast] theorem ofNat_emod (m n : Nat) : ((m % n) : Int) = m % n := rfl
@[simp] theorem ofNat_emod (m n : Nat) : ((m % n) : Int) = m % n := rfl
@[simp] theorem zero_emod (b : Int) : 0 % b = 0 := by simp [mod_def', emod]
@@ -260,7 +260,7 @@ protected theorem dvd_sub : ∀ {a b c : Int}, a b → a c → a b -
| _, _, _, d, rfl, e, rfl => d - e, by rw [Int.mul_sub]
@[norm_cast] theorem ofNat_dvd {m n : Nat} : (m : Int) n m n := by
theorem ofNat_dvd {m n : Nat} : (m : Int) n m n := by
refine fun a, ae => ?_, fun k, e => k, by rw [e, Int.ofNat_mul]
match Int.le_total a 0 with
| .inl h =>
@@ -325,78 +325,23 @@ theorem sub_ediv_of_dvd (a : Int) {b c : Int}
rw [Int.sub_eq_add_neg, Int.sub_eq_add_neg, Int.add_ediv_of_dvd_right (Int.dvd_neg.2 hcb)]
congr; exact Int.neg_ediv_of_dvd hcb
@[simp] theorem ediv_one : a : Int, a / 1 = a
| (_:Nat) => congrArg Nat.cast (Nat.div_one _)
| -[_+1] => congrArg negSucc (Nat.div_one _)
/-!
# `bmod` ("balanced" mod)
@[simp] theorem emod_one (a : Int) : a % 1 = 0 := by
simp [emod_def, Int.one_mul, Int.sub_self]
We use balanced mod in the omega algorithm,
to make ±1 coefficients appear in equations without them.
-/
@[simp] protected theorem ediv_self {a : Int} (H : a 0) : a / a = 1 := by
have := Int.mul_ediv_cancel 1 H; rwa [Int.one_mul] at this
@[simp]
theorem Int.emod_sub_cancel (x y : Int): (x - y)%y = x%y := by
if h : y = 0 then
simp [h]
/--
Balanced mod, taking values in the range [- m/2, (m - 1)/2].
-/
def bmod (x : Int) (m : Nat) : Int :=
let r := x % m
if r < (m + 1) / 2 then
r
else
simp only [Int.emod_def, Int.sub_ediv_of_dvd, Int.dvd_refl, Int.ediv_self h, Int.mul_sub]
simp [Int.mul_one, Int.sub_sub, Int.add_comm y]
/-! bmod -/
r - m
@[simp] theorem bmod_emod : bmod x m % m = x % m := by
dsimp [bmod]
split <;> simp [Int.sub_emod]
@[simp]
theorem emod_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n) n = Int.bmod x n := by
simp [bmod, Int.emod_emod]
theorem bmod_def (x : Int) (m : Nat) : bmod x m =
if (x % m) < (m + 1) / 2 then
x % m
else
(x % m) - m :=
rfl
theorem bmod_pos (x : Int) (m : Nat) (p : x % m < (m + 1) / 2) : bmod x m = x % m := by
simp [bmod_def, p]
theorem bmod_neg (x : Int) (m : Nat) (p : x % m (m + 1) / 2) : bmod x m = (x % m) - m := by
simp [bmod_def, Int.not_lt.mpr p]
@[simp]
theorem bmod_one_is_zero (x : Int) : Int.bmod x 1 = 0 := by
simp [Int.bmod]
@[simp]
theorem bmod_add_cancel (x : Int) (n : Nat) : Int.bmod (x + n) n = Int.bmod x n := by
simp [bmod_def]
@[simp]
theorem bmod_add_mul_cancel (x : Int) (n : Nat) (k : Int) : Int.bmod (x + n * k) n = Int.bmod x n := by
simp [bmod_def]
@[simp]
theorem bmod_sub_cancel (x : Int) (n : Nat) : Int.bmod (x - n) n = Int.bmod x n := by
simp [bmod_def]
@[simp]
theorem emod_add_bmod_congr (x : Int) (n : Nat) : Int.bmod (x%n + y) n = Int.bmod (x + y) n := by
simp [Int.emod_def, Int.sub_eq_add_neg]
rw [Int.mul_neg, Int.add_right_comm, Int.bmod_add_mul_cancel]
@[simp]
theorem bmod_add_bmod_congr : Int.bmod (Int.bmod x n + y) n = Int.bmod (x + y) n := by
rw [bmod_def x n]
split
case inl p =>
simp
case inr p =>
rw [Int.sub_eq_add_neg, Int.add_right_comm, Int.sub_eq_add_neg]
simp
@[simp]
theorem add_bmod_bmod : Int.bmod (x + Int.bmod y n) n = Int.bmod (x + y) n := by
rw [Int.add_comm x, Int.bmod_add_bmod_congr, Int.add_comm y]

View File

@@ -22,8 +22,8 @@ theorem subNatNat_of_sub_eq_succ {m n k : Nat} (h : n - m = succ k) : subNatNat
@[simp] protected theorem neg_zero : -(0:Int) = 0 := rfl
@[norm_cast] theorem ofNat_add (n m : Nat) : ((n + m) : Int) = n + m := rfl
@[norm_cast] theorem ofNat_mul (n m : Nat) : ((n * m) : Int) = n * m := rfl
theorem ofNat_add (n m : Nat) : ((n + m) : Int) = n + m := rfl
theorem ofNat_mul (n m : Nat) : ((n * m) : Int) = n * m := rfl
theorem ofNat_succ (n : Nat) : (succ n : Int) = n + 1 := rfl
@[local simp] theorem neg_ofNat_zero : -((0 : Nat) : Int) = 0 := rfl
@@ -53,7 +53,7 @@ theorem negOfNat_eq : negOfNat n = -ofNat n := rfl
/- ## some basic functions and properties -/
@[norm_cast] theorem ofNat_inj : ((m : Nat) : Int) = (n : Nat) m = n := ofNat.inj, congrArg _
theorem ofNat_inj : ((m : Nat) : Int) = (n : Nat) m = n := ofNat.inj, congrArg _
theorem ofNat_eq_zero : ((n : Nat) : Int) = 0 n = 0 := ofNat_inj
@@ -67,7 +67,7 @@ theorem negSucc_eq (n : Nat) : -[n+1] = -((n : Int) + 1) := rfl
@[simp] theorem zero_ne_negSucc (n : Nat) : 0 -[n+1] := nofun
@[simp, norm_cast] theorem Nat.cast_ofNat_Int :
@[simp] theorem Nat.cast_ofNat_Int :
(Nat.cast (no_index (OfNat.ofNat n)) : Int) = OfNat.ofNat n := rfl
/- ## neg -/
@@ -295,7 +295,7 @@ protected theorem sub_neg (a b : Int) : a - -b = a + b := by simp [Int.sub_eq_ad
protected theorem add_sub_assoc (a b c : Int) : a + b - c = a + (b - c) := by
rw [Int.sub_eq_add_neg, Int.add_assoc, Int.sub_eq_add_neg]
@[norm_cast] theorem ofNat_sub (h : m n) : ((n - m : Nat) : Int) = n - m := by
theorem ofNat_sub (h : m n) : ((n - m : Nat) : Int) = n - m := by
match m with
| 0 => rfl
| succ m =>
@@ -321,27 +321,6 @@ theorem toNat_sub (m n : Nat) : toNat (m - n) = m - n := by
· exact (Nat.add_sub_cancel_left ..).symm
· dsimp; rw [Nat.add_assoc, Nat.sub_eq_zero_of_le (Nat.le_add_right ..)]; rfl
/- ## add/sub injectivity -/
@[simp]
protected theorem add_right_inj (i j k : Int) : (i + k = j + k) i = j := by
apply Iff.intro
· intro p
rw [Int.add_sub_cancel i k, Int.add_sub_cancel j k, p]
· exact congrArg (· + k)
@[simp]
protected theorem add_left_inj (i j k : Int) : (k + i = k + j) i = j := by
simp [Int.add_comm k]
@[simp]
protected theorem sub_left_inj (i j k : Int) : (k - i = k - j) i = j := by
simp [Int.sub_eq_add_neg, Int.neg_inj]
@[simp]
protected theorem sub_right_inj (i j k : Int) : (i - k = j - k) i = j := by
simp [Int.sub_eq_add_neg]
/- ## Ring properties -/
@[simp] theorem ofNat_mul_negSucc (m n : Nat) : (m : Int) * -[n+1] = -(m * succ n) := rfl
@@ -499,33 +478,6 @@ theorem eq_one_of_mul_eq_self_left {a b : Int} (Hpos : a ≠ 0) (H : b * a = a)
theorem eq_one_of_mul_eq_self_right {a b : Int} (Hpos : b 0) (H : b * a = b) : a = 1 :=
Int.eq_of_mul_eq_mul_left Hpos <| by rw [Int.mul_one, H]
/-! # pow -/
protected theorem pow_zero (b : Int) : b^0 = 1 := rfl
protected theorem pow_succ (b : Int) (e : Nat) : b ^ (e+1) = (b ^ e) * b := rfl
protected theorem pow_succ' (b : Int) (e : Nat) : b ^ (e+1) = b * (b ^ e) := by
rw [Int.mul_comm, Int.pow_succ]
theorem pow_le_pow_of_le_left {n m : Nat} (h : n m) : (i : Nat), n^i m^i
| 0 => Nat.le_refl _
| succ i => Nat.mul_le_mul (pow_le_pow_of_le_left h i) h
theorem pow_le_pow_of_le_right {n : Nat} (hx : n > 0) {i : Nat} : {j}, i j n^i n^j
| 0, h =>
have : i = 0 := eq_zero_of_le_zero h
this.symm Nat.le_refl _
| succ j, h =>
match le_or_eq_of_le_succ h with
| Or.inl h => show n^i n^j * n from
have : n^i * 1 n^j * n := Nat.mul_le_mul (pow_le_pow_of_le_right hx h) hx
Nat.mul_one (n^i) this
| Or.inr h =>
h.symm Nat.le_refl _
theorem pos_pow_of_pos {n : Nat} (m : Nat) (h : 0 < n) : 0 < n^m :=
pow_le_pow_of_le_right h (Nat.zero_le _)
/-! NatCast lemmas -/
/-!
@@ -545,10 +497,4 @@ theorem natCast_one : ((1 : Nat) : Int) = (1 : Int) := rfl
@[simp] theorem natCast_mul (a b : Nat) : ((a * b : Nat) : Int) = (a : Int) * (b : Int) := by
simp
theorem natCast_pow (b n : Nat) : ((b^n : Nat) : Int) = (b : Int) ^ n := by
match n with
| 0 => rfl
| n + 1 =>
simp only [Nat.pow_succ, Int.pow_succ, natCast_mul, natCast_pow _ n]
end Int

View File

@@ -48,7 +48,7 @@ protected theorem le_total (a b : Int) : a ≤ b b ≤ a :=
(nonneg_or_nonneg_neg (b - a)).imp_right fun H => by
rwa [show -(b - a) = a - b by simp [Int.add_comm, Int.sub_eq_add_neg]] at H
@[simp, norm_cast] theorem ofNat_le {m n : Nat} : (m : Int) n m n :=
@[simp] theorem ofNat_le {m n : Nat} : (m : Int) n m n :=
fun h =>
let k, hk := le.dest h
Nat.le.intro <| Int.ofNat.inj <| (Int.ofNat_add m k).trans hk,
@@ -74,10 +74,10 @@ theorem lt.intro {a b : Int} {n : Nat} (h : a + Nat.succ n = b) : a < b :=
theorem lt.dest {a b : Int} (h : a < b) : n : Nat, a + Nat.succ n = b :=
let n, h := le.dest h; n, by rwa [Int.add_comm, Int.add_left_comm] at h
@[simp, norm_cast] theorem ofNat_lt {n m : Nat} : (n : Int) < m n < m := by
@[simp] theorem ofNat_lt {n m : Nat} : (n : Int) < m n < m := by
rw [lt_iff_add_one_le, ofNat_succ, ofNat_le]; rfl
@[simp, norm_cast] theorem ofNat_pos {n : Nat} : 0 < (n : Int) 0 < n := ofNat_lt
@[simp] theorem ofNat_pos {n : Nat} : 0 < (n : Int) 0 < n := ofNat_lt
theorem ofNat_nonneg (n : Nat) : 0 (n : Int) := _
@@ -192,11 +192,6 @@ protected theorem min_le_right (a b : Int) : min a b ≤ b := by rw [Int.min_def
protected theorem min_le_left (a b : Int) : min a b a := Int.min_comm .. Int.min_le_right ..
protected theorem min_eq_left {a b : Int} (h : a b) : min a b = a := by simp [Int.min_def, h]
protected theorem min_eq_right {a b : Int} (h : b a) : min a b = b := by
rw [Int.min_comm a b]; exact Int.min_eq_left h
protected theorem le_min {a b c : Int} : a min b c a b a c :=
fun h => Int.le_trans h (Int.min_le_left ..), Int.le_trans h (Int.min_le_right ..),
fun h₁, h₂ => by rw [Int.min_def]; split <;> assumption
@@ -215,12 +210,6 @@ protected theorem max_le {a b c : Int} : max a b ≤ c ↔ a ≤ c ∧ b ≤ c :
fun h => Int.le_trans (Int.le_max_left ..) h, Int.le_trans (Int.le_max_right ..) h,
fun h₁, h₂ => by rw [Int.max_def]; split <;> assumption
protected theorem max_eq_right {a b : Int} (h : a b) : max a b = b := by
simp [Int.max_def, h, Int.not_lt.2 h]
protected theorem max_eq_left {a b : Int} (h : b a) : max a b = a := by
rw [ Int.max_comm b a]; exact Int.max_eq_right h
theorem eq_natAbs_of_zero_le {a : Int} (h : 0 a) : a = natAbs a := by
let n, e := eq_ofNat_of_zero_le h
rw [e]; rfl
@@ -447,54 +436,3 @@ theorem natAbs_of_nonneg {a : Int} (H : 0 ≤ a) : (natAbs a : Int) = a :=
theorem ofNat_natAbs_of_nonpos {a : Int} (H : a 0) : (natAbs a : Int) = -a := by
rw [ natAbs_neg, natAbs_of_nonneg (Int.neg_nonneg_of_nonpos H)]
/-! ### toNat -/
theorem toNat_eq_max : a : Int, (toNat a : Int) = max a 0
| (n : Nat) => (Int.max_eq_left (ofNat_zero_le n)).symm
| -[n+1] => (Int.max_eq_right (Int.le_of_lt (negSucc_lt_zero n))).symm
@[simp] theorem toNat_zero : (0 : Int).toNat = 0 := rfl
@[simp] theorem toNat_one : (1 : Int).toNat = 1 := rfl
@[simp] theorem toNat_of_nonneg {a : Int} (h : 0 a) : (toNat a : Int) = a := by
rw [toNat_eq_max, Int.max_eq_left h]
@[simp] theorem toNat_ofNat (n : Nat) : toNat n = n := rfl
@[simp] theorem toNat_ofNat_add_one {n : Nat} : ((n : Int) + 1).toNat = n + 1 := rfl
theorem self_le_toNat (a : Int) : a toNat a := by rw [toNat_eq_max]; apply Int.le_max_left
@[simp] theorem le_toNat {n : Nat} {z : Int} (h : 0 z) : n z.toNat (n : Int) z := by
rw [ Int.ofNat_le, Int.toNat_of_nonneg h]
@[simp] theorem toNat_lt {n : Nat} {z : Int} (h : 0 z) : z.toNat < n z < (n : Int) := by
rw [ Int.not_le, Nat.not_le, Int.le_toNat h]
theorem toNat_add {a b : Int} (ha : 0 a) (hb : 0 b) : (a + b).toNat = a.toNat + b.toNat :=
match a, b, eq_ofNat_of_zero_le ha, eq_ofNat_of_zero_le hb with
| _, _, _, rfl, _, rfl => rfl
theorem toNat_add_nat {a : Int} (ha : 0 a) (n : Nat) : (a + n).toNat = a.toNat + n :=
match a, eq_ofNat_of_zero_le ha with | _, _, rfl => rfl
@[simp] theorem pred_toNat : i : Int, (i - 1).toNat = i.toNat - 1
| 0 => rfl
| (n+1:Nat) => by simp [ofNat_add]
| -[n+1] => rfl
@[simp] theorem toNat_sub_toNat_neg : n : Int, n.toNat - (-n).toNat = n
| 0 => rfl
| (_+1:Nat) => Int.sub_zero _
| -[_+1] => Int.zero_sub _
@[simp] theorem toNat_add_toNat_neg_eq_natAbs : n : Int, n.toNat + (-n).toNat = n.natAbs
| 0 => rfl
| (_+1:Nat) => Nat.add_zero _
| -[_+1] => Nat.zero_add _
@[simp] theorem toNat_neg_nat : n : Nat, (-(n : Int)).toNat = 0
| 0 => rfl
| _+1 => rfl

View File

@@ -5,7 +5,6 @@ Author: Leonardo de Moura
-/
prelude
import Init.Data.Nat.Linear
import Init.Data.Array.Basic
import Init.Data.List.Basic
import Init.Util
@@ -208,42 +207,4 @@ if the result of each `f a` is a pointer equal value `a`.
def mapMono (as : List α) (f : α α) : List α :=
Id.run <| as.mapMonoM f
/--
Monadic generalization of `List.partition`.
This uses `Array.toList` and which isn't imported by `Init.Data.List.Basic`.
-/
@[inline] def partitionM [Monad m] (p : α m Bool) (l : List α) : m (List α × List α) :=
go l #[] #[]
where
/-- Auxiliary for `partitionM`:
`partitionM.go p l acc₁ acc₂` returns `(acc₁.toList ++ left, acc₂.toList ++ right)`
if `partitionM p l` returns `(left, right)`. -/
@[specialize] go : List α Array α Array α m (List α × List α)
| [], acc₁, acc₂ => pure (acc₁.toList, acc₂.toList)
| x :: xs, acc₁, acc₂ => do
if p x then
go xs (acc₁.push x) acc₂
else
go xs acc₁ (acc₂.push x)
/--
Given a function `f : α → β ⊕ γ`, `partitionMap f l` maps the list by `f`
whilst partitioning the result it into a pair of lists, `List β × List γ`,
partitioning the `.inl _` into the left list, and the `.inr _` into the right List.
```
partitionMap (id : Nat ⊕ Nat → Nat ⊕ Nat) [inl 0, inr 1, inl 2] = ([0, 2], [1])
```
-/
@[inline] def partitionMap (f : α β γ) (l : List α) : List β × List γ := go l #[] #[] where
/-- Auxiliary for `partitionMap`:
`partitionMap.go f l acc₁ acc₂ = (acc₁.toList ++ left, acc₂.toList ++ right)`
if `partitionMap f l = (left, right)`. -/
@[specialize] go : List α Array β Array γ List β × List γ
| [], acc₁, acc₂ => (acc₁.toList, acc₂.toList)
| x :: xs, acc₁, acc₂ =>
match f x with
| .inl a => go xs (acc₁.push a) acc₂
| .inr b => go xs acc₁ (acc₂.push b)
end List

View File

@@ -6,7 +6,6 @@ Authors: Parikshit Khanna, Jeremy Avigad, Leonardo de Moura, Floris van Doorn, M
prelude
import Init.Data.List.BasicAux
import Init.Data.List.Control
import Init.Data.Nat.Lemmas
import Init.PropLemmas
import Init.Control.Lawful
import Init.Hints
@@ -106,11 +105,6 @@ theorem append_left_inj {s₁ s₂ : List α} (t) : s₁ ++ t = s₂ ++ t ↔ s
@[simp] theorem append_eq_nil : p ++ q = [] p = [] q = [] := by
cases p <;> simp
theorem get_append : {l₁ l₂ : List α} (n : Nat) (h : n < l₁.length),
(l₁ ++ l₂).get n, length_append .. Nat.lt_add_right _ h = l₁.get n, h
| a :: l, _, 0, h => rfl
| a :: l, _, n+1, h => by simp only [get, cons_append]; apply get_append
/-! ### map -/
@[simp] theorem map_nil {f : α β} : map f [] = [] := rfl
@@ -210,12 +204,6 @@ theorem get?_eq_some : l.get? n = some a ↔ ∃ h, get l ⟨n, h⟩ = a :=
| _ :: _, 0 => rfl
| _ :: l, n+1 => get?_map f l n
theorem get?_append {l₁ l₂ : List α} {n : Nat} (hn : n < l₁.length) :
(l₁ ++ l₂).get? n = l₁.get? n := by
have hn' : n < (l₁ ++ l₂).length := Nat.lt_of_lt_of_le hn <|
length_append .. Nat.le_add_right ..
rw [get?_eq_get hn, get?_eq_get hn', get_append]
@[simp] theorem get?_concat_length : (l : List α) (a : α), (l ++ [a]).get? l.length = some a
| [], a => rfl
| b :: l, a => by rw [cons_append, length_cons]; simp only [get?, get?_concat_length]
@@ -242,31 +230,6 @@ theorem getLast?_eq_get? : ∀ (l : List α), getLast? l = l.get? (l.length - 1)
@[simp] theorem getLast?_concat (l : List α) : getLast? (l ++ [a]) = some a := by
simp [getLast?_eq_get?, Nat.succ_sub_succ]
theorem getD_eq_get? : l n (a : α), getD l n a = (get? l n).getD a
| [], _, _ => rfl
| _a::_, 0, _ => rfl
| _::l, _+1, _ => getD_eq_get? (l := l) ..
theorem get?_append_right : {l₁ l₂ : List α} {n : Nat}, l₁.length n
(l₁ ++ l₂).get? n = l₂.get? (n - l₁.length)
| [], _, n, _ => rfl
| a :: l, _, n+1, h₁ => by rw [cons_append]; simp [get?_append_right (Nat.lt_succ.1 h₁)]
theorem get?_reverse' : {l : List α} (i j), i + j + 1 = length l
get? l.reverse i = get? l j
| [], _, _, _ => rfl
| a::l, i, 0, h => by simp at h; simp [h, get?_append_right]
| a::l, i, j+1, h => by
have := Nat.succ.inj h; simp at this
rw [get?_append, get?_reverse' _ j this]
rw [length_reverse, this]; apply Nat.lt_add_of_pos_right (Nat.succ_pos _)
theorem get?_reverse {l : List α} (i) (h : i < length l) :
get? l.reverse i = get? l (l.length - 1 - i) :=
get?_reverse' _ _ <| by
rw [Nat.add_sub_of_le (Nat.le_sub_one_of_lt h),
Nat.sub_add_cancel (Nat.lt_of_le_of_lt (Nat.zero_le _) h)]
/-! ### take and drop -/
@[simp] theorem take_append_drop : (n : Nat) (l : List α), take n l ++ drop n l = l
@@ -665,44 +628,3 @@ theorem minimum?_eq_some_iff [Min α] [LE α] [anti : Antisymm ((· : α) ≤ ·
exact congrArg some <| anti.1
((le_minimum?_iff le_min_iff (xs := x::xs) rfl _).1 (le_refl _) _ h₁)
(h₂ _ (minimum?_mem min_eq_or (xs := x::xs) rfl))
@[simp] theorem get_cons_succ {as : List α} {h : i + 1 < (a :: as).length} :
(a :: as).get i+1, h = as.get i, Nat.lt_of_succ_lt_succ h := rfl
@[simp] theorem get_cons_succ' {as : List α} {i : Fin as.length} :
(a :: as).get i.succ = as.get i := rfl
@[simp] theorem set_nil (n : Nat) (a : α) : [].set n a = [] := rfl
@[simp] theorem set_zero (x : α) (xs : List α) (a : α) :
(x :: xs).set 0 a = a :: xs := rfl
@[simp] theorem set_succ (x : α) (xs : List α) (n : Nat) (a : α) :
(x :: xs).set n.succ a = x :: xs.set n a := rfl
@[simp] theorem get_set_eq (l : List α) (i : Nat) (a : α) (h : i < (l.set i a).length) :
(l.set i a).get i, h = a :=
match l, i with
| [], _ => by
simp at h
contradiction
| _ :: _, 0 => by
simp
| _ :: l, i + 1 => by
simp [get_set_eq l]
@[simp] theorem get_set_ne (l : List α) {i j : Nat} (h : i j) (a : α)
(hj : j < (l.set i a).length) :
(l.set i a).get j, hj = l.get j, by simp at hj; exact hj :=
match l, i, j with
| [], _, _ => by
simp
| _ :: _, 0, 0 => by
contradiction
| _ :: _, 0, _ + 1 => by
simp
| _ :: _, _ + 1, 0 => by
simp
| _ :: l, i + 1, j + 1 => by
have g : i j := h congrArg (· + 1)
simp [get_set_ne l g]

View File

@@ -189,7 +189,7 @@ protected theorem mul_comm : ∀ (n m : Nat), n * m = m * n
Nat.mul_comm n 1 Nat.mul_one n
protected theorem left_distrib (n m k : Nat) : n * (m + k) = n * m + n * k := by
induction n with
induction n generalizing m k with
| zero => repeat rw [Nat.zero_mul]
| succ n ih => simp [succ_mul, ih]; rw [Nat.add_assoc, Nat.add_assoc (n*m)]; apply congrArg; apply Nat.add_left_comm
@@ -503,10 +503,10 @@ theorem eq_of_mul_eq_mul_right {n m k : Nat} (hm : 0 < m) (h : n * m = k * m) :
/-! # power -/
protected theorem pow_succ (n m : Nat) : n^(succ m) = n^m * n :=
theorem pow_succ (n m : Nat) : n^(succ m) = n^m * n :=
rfl
protected theorem pow_zero (n : Nat) : n^0 = 1 := rfl
theorem pow_zero (n : Nat) : n^0 = 1 := rfl
theorem pow_le_pow_of_le_left {n m : Nat} (h : n m) : (i : Nat), n^i m^i
| 0 => Nat.le_refl _

View File

@@ -1,8 +1,3 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Data.Nat.Bitwise.Basic
import Init.Data.Nat.Bitwise.Lemmas

View File

@@ -1,5 +1,6 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Copyright (c) 2023 by the authors listed in the file AUTHORS and their
institutional affiliations. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix
-/
@@ -239,7 +240,7 @@ theorem testBit_two_pow_add_gt {i j : Nat} (j_lt_i : j < i) (x : Nat) :
rw [Nat.sub_eq_zero_iff_le] at i_sub_j_eq
exact Nat.not_le_of_gt j_lt_i i_sub_j_eq
| d+1 =>
simp [Nat.pow_succ, Nat.mul_comm _ 2, Nat.mul_add_mod]
simp [pow_succ, Nat.mul_comm _ 2, Nat.mul_add_mod]
@[simp] theorem testBit_mod_two_pow (x j i : Nat) :
testBit (x % 2^j) i = (decide (i < j) && testBit x i) := by
@@ -287,7 +288,7 @@ theorem testBit_two_pow_sub_succ (h₂ : x < 2 ^ n) (i : Nat) :
simp only [testBit_succ]
match n with
| 0 =>
simp only [Nat.pow_zero, succ_sub_succ_eq_sub, Nat.zero_sub, Nat.zero_div, zero_testBit]
simp only [pow_zero, succ_sub_succ_eq_sub, Nat.zero_sub, Nat.zero_div, zero_testBit]
rw [decide_eq_false] <;> simp
| n+1 =>
rw [Nat.two_pow_succ_sub_succ_div_two, ih]
@@ -352,7 +353,7 @@ private theorem eq_0_of_lt (x : Nat) : x < 2^ 0 ↔ x = 0 := eq_0_of_lt_one x
private theorem zero_lt_pow (n : Nat) : 0 < 2^n := by
induction n
case zero => simp [eq_0_of_lt]
case succ n hyp => simpa [Nat.pow_succ]
case succ n hyp => simpa [pow_succ]
private theorem div_two_le_of_lt_two {m n : Nat} (p : m < 2 ^ succ n) : m / 2 < 2^n := by
simp [div_lt_iff_lt_mul Nat.zero_lt_two]
@@ -377,7 +378,7 @@ theorem bitwise_lt_two_pow (left : x < 2^n) (right : y < 2^n) : (Nat.bitwise f x
simp only [x_zero, y_zero, if_neg]
have hyp1 := hyp (div_two_le_of_lt_two left) (div_two_le_of_lt_two right)
by_cases p : f (decide (x % 2 = 1)) (decide (y % 2 = 1)) = true <;>
simp [p, Nat.pow_succ, mul_succ, Nat.add_assoc]
simp [p, pow_succ, mul_succ, Nat.add_assoc]
case pos =>
apply lt_of_succ_le
simp only [ Nat.succ_add]

View File

@@ -1,8 +1,3 @@
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
-/
prelude
import Init.Data.Nat.Div
@@ -90,6 +85,7 @@ theorem emod_pos_of_not_dvd {a b : Nat} (h : ¬ a b) : 0 < b % a := by
rw [dvd_iff_mod_eq_zero] at h
exact Nat.pos_of_ne_zero h
protected theorem mul_div_cancel' {n m : Nat} (H : n m) : n * (m / n) = m := by
have := mod_add_div m n
rwa [mod_eq_zero_of_dvd H, Nat.zero_add] at this

View File

@@ -742,7 +742,7 @@ theorem shiftLeft_eq (a b : Nat) : a <<< b = a * 2 ^ b :=
match b with
| 0 => (Nat.mul_one _).symm
| b+1 => (shiftLeft_eq _ b).trans <| by
simp [Nat.pow_succ, Nat.mul_assoc, Nat.mul_left_comm, Nat.mul_comm]
simp [pow_succ, Nat.mul_assoc, Nat.mul_left_comm, Nat.mul_comm]
theorem one_shiftLeft (n : Nat) : 1 <<< n = 2 ^ n := by rw [shiftLeft_eq, Nat.one_mul]

View File

@@ -1,8 +1,3 @@
/-
Copyright (c) 2016 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Mario Carneiro
-/
prelude
import Init.ByCases

View File

@@ -290,40 +290,17 @@ where go (acc : String) (s : String) : List String → String
| a :: as => go (acc ++ s ++ a) s as
| [] => acc
/-- Iterator over the characters (`Char`) of a `String`.
Typically created by `s.iter`, where `s` is a `String`.
An iterator is *valid* if the position `i` is *valid* for the string `s`, meaning `0 ≤ i ≤ s.endPos`
and `i` lies on a UTF8 byte boundary. If `i = s.endPos`, the iterator is at the end of the string.
Most operations on iterators return arbitrary values if the iterator is not valid. The functions in
the `String.Iterator` API should rule out the creation of invalid iterators, with two exceptions:
- `Iterator.next iter` is invalid if `iter` is already at the end of the string (`iter.atEnd` is
`true`), and
- `Iterator.forward iter n`/`Iterator.nextn iter n` is invalid if `n` is strictly greater than the
number of remaining characters.
-/
/-- Iterator for `String`. That is, a `String` and a position in that string. -/
structure Iterator where
/-- The string the iterator is for. -/
s : String
/-- The current position.
This position is not necessarily valid for the string, for instance if one keeps calling
`Iterator.next` when `Iterator.atEnd` is true. If the position is not valid, then the
current character is `(default : Char)`, similar to `String.get` on an invalid position. -/
i : Pos
deriving DecidableEq
/-- Creates an iterator at the beginning of a string. -/
def mkIterator (s : String) : Iterator :=
s, 0
@[inherit_doc mkIterator]
abbrev iter := mkIterator
/-- The size of a string iterator is the number of bytes remaining. -/
instance : SizeOf String.Iterator where
sizeOf i := i.1.utf8ByteSize - i.2.byteIdx
@@ -331,90 +308,55 @@ theorem Iterator.sizeOf_eq (i : String.Iterator) : sizeOf i = i.1.utf8ByteSize -
rfl
namespace Iterator
@[inherit_doc Iterator.s]
def toString := Iterator.s
def toString : Iterator String
| s, _ => s
/-- Number of bytes remaining in the iterator. -/
def remainingBytes : Iterator Nat
| s, i => s.endPos.byteIdx - i.byteIdx
@[inherit_doc Iterator.i]
def pos := Iterator.i
def pos : Iterator Pos
| _, i => i
/-- The character at the current position.
On an invalid position, returns `(default : Char)`. -/
def curr : Iterator Char
| s, i => get s i
/-- Moves the iterator's position forward by one character, unconditionally.
It is only valid to call this function if the iterator is not at the end of the string, *i.e.*
`Iterator.atEnd` is `false`; otherwise, the resulting iterator will be invalid. -/
def next : Iterator Iterator
| s, i => s, s.next i
/-- Decreases the iterator's position.
If the position is zero, this function is the identity. -/
def prev : Iterator Iterator
| s, i => s, s.prev i
/-- True if the iterator is past the string's last character. -/
def atEnd : Iterator Bool
| s, i => i.byteIdx s.endPos.byteIdx
/-- True if the iterator is not past the string's last character. -/
def hasNext : Iterator Bool
| s, i => i.byteIdx < s.endPos.byteIdx
/-- True if the position is not zero. -/
def hasPrev : Iterator Bool
| _, i => i.byteIdx > 0
/-- Replaces the current character in the string.
Does nothing if the iterator is at the end of the string. If the iterator contains the only
reference to its string, this function will mutate the string in-place instead of allocating a new
one. -/
def setCurr : Iterator Char Iterator
| s, i, c => s.set i c, i
/-- Moves the iterator's position to the end of the string.
Note that `i.toEnd.atEnd` is always `true`. -/
def toEnd : Iterator Iterator
| s, _ => s, s.endPos
/-- Extracts the substring between the positions of two iterators.
Returns the empty string if the iterators are for different strings, or if the position of the first
iterator is past the position of the second iterator. -/
def extract : Iterator Iterator String
| s₁, b, s₂, e =>
if s₁ s₂ || b > e then ""
else s₁.extract b e
/-- Moves the iterator's position several characters forward.
The resulting iterator is only valid if the number of characters to skip is less than or equal to
the number of characters left in the iterator. -/
def forward : Iterator Nat Iterator
| it, 0 => it
| it, n+1 => forward it.next n
/-- The remaining characters in an iterator, as a string. -/
def remainingToString : Iterator String
| s, i => s.extract i s.endPos
@[inherit_doc forward]
def nextn : Iterator Nat Iterator
| it, 0 => it
| it, i+1 => nextn it.next i
/-- Moves the iterator's position several characters back.
If asked to go back more characters than available, stops at the beginning of the string. -/
def prevn : Iterator Nat Iterator
| it, 0 => it
| it, i+1 => prevn it.prev i

View File

@@ -4,7 +4,12 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
-/
prelude
import Init.Control.Except
import Init.Data.ByteArray
import Init.SimpLemmas
import Init.Data.Nat.Linear
import Init.Util
import Init.WFTactics
namespace String

View File

@@ -1,24 +0,0 @@
/-
Copyright (c) 2017 Mario Carneiro. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Mario Carneiro, Yury G. Kudryashov
-/
prelude
import Init.Core
namespace Sum
deriving instance DecidableEq for Sum
deriving instance BEq for Sum
/-- Check if a sum is `inl` and if so, retrieve its contents. -/
def getLeft? : α β Option α
| inl a => some a
| inr _ => none
/-- Check if a sum is `inr` and if so, retrieve its contents. -/
def getRight? : α β Option β
| inr b => some b
| inl _ => none
end Sum

View File

@@ -9,7 +9,6 @@ prelude
import Init.MetaTypes
import Init.Data.Array.Basic
import Init.Data.Option.BasicAux
import Init.Data.String.Extra
namespace Lean
@@ -106,42 +105,6 @@ def idEndEscape := '»'
def isIdBeginEscape (c : Char) : Bool := c = idBeginEscape
def isIdEndEscape (c : Char) : Bool := c = idEndEscape
private def findLeadingSpacesSize (s : String) : Nat :=
let it := s.iter
let it := it.find (· == '\n') |>.next
consumeSpaces it 0 s.length
where
consumeSpaces (it : String.Iterator) (curr min : Nat) : Nat :=
if it.atEnd then min
else if it.curr == ' ' || it.curr == '\t' then consumeSpaces it.next (curr + 1) min
else if it.curr == '\n' then findNextLine it.next min
else findNextLine it.next (Nat.min curr min)
findNextLine (it : String.Iterator) (min : Nat) : Nat :=
if it.atEnd then min
else if it.curr == '\n' then consumeSpaces it.next 0 min
else findNextLine it.next min
private def removeNumLeadingSpaces (n : Nat) (s : String) : String :=
consumeSpaces n s.iter ""
where
consumeSpaces (n : Nat) (it : String.Iterator) (r : String) : String :=
match n with
| 0 => saveLine it r
| n+1 =>
if it.atEnd then r
else if it.curr == ' ' || it.curr == '\t' then consumeSpaces n it.next r
else saveLine it r
termination_by (it, 1)
saveLine (it : String.Iterator) (r : String) : String :=
if it.atEnd then r
else if it.curr == '\n' then consumeSpaces n it.next (r.push '\n')
else saveLine it.next (r.push it.curr)
termination_by (it, 0)
def removeLeadingSpaces (s : String) : String :=
let n := findLeadingSpacesSize s
if n == 0 then s else removeNumLeadingSpaces n s
namespace Name
def getRoot : Name Name
@@ -1298,11 +1261,6 @@ def expandInterpolatedStr (interpStr : TSyntax interpolatedStrKind) (type : Term
let r ← expandInterpolatedStrChunks interpStr.raw.getArgs (fun a b => `($a ++ $b)) (fun a => `($toTypeFn $a))
`(($r : $type))
def getDocString (stx : TSyntax `Lean.Parser.Command.docComment) : String :=
match stx.raw[1] with
| Syntax.atom _ val => val.extract 0 (val.endPos - ⟨2⟩)
| _ => ""
end TSyntax
namespace Meta
@@ -1362,24 +1320,9 @@ structure OmegaConfig where
end Omega
namespace CheckTactic
/--
Type used to lift an arbitrary value into a type parameter so it can
appear in a proof goal.
It is used by the #check_tactic command.
-/
inductive CheckGoalType {α : Sort u} : (val : α) → Prop where
| intro : (val : α) → CheckGoalType val
end CheckTactic
end Meta
namespace Parser
namespace Tactic
namespace Parser.Tactic
/-- `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`
@@ -1440,8 +1383,6 @@ This will rewrite with all equation lemmas, which can be used to
partially evaluate many definitions. -/
declare_simp_like_tactic (dsimp := true) dsimpAutoUnfold "dsimp! " fun (c : Lean.Meta.DSimp.Config) => { c with autoUnfold := true }
end Tactic
end Parser
end Parser.Tactic
end Lean

View File

@@ -484,9 +484,6 @@ instance : Coe Syntax (TSyntax `rawStx) where
/-- `with_annotate_term stx e` annotates the lexical range of `stx : Syntax` with term info for `e`. -/
scoped syntax (name := withAnnotateTerm) "with_annotate_term " rawStx ppSpace term : term
/-- Normalize casts in an expression using the same method as the `norm_cast` tactic. -/
syntax (name := modCast) "mod_cast " term : term
/--
The attribute `@[deprecated]` on a declaration indicates that the declaration
is discouraged for use in new code, and/or should be migrated away from in
@@ -503,25 +500,6 @@ applications of this function as `↑` when printing expressions.
-/
syntax (name := Attr.coe) "coe" : attr
/--
This attribute marks a code action, which is used to suggest new tactics or replace existing ones.
* `@[command_code_action kind]`: This is a code action which applies to applications of the command
`kind` (a command syntax kind), which can replace the command or insert things before or after it.
* `@[command_code_action kind₁ kind₂]`: shorthand for
`@[command_code_action kind₁, command_code_action kind₂]`.
* `@[command_code_action]`: This is a command code action that applies to all commands.
Use sparingly.
-/
syntax (name := command_code_action) "command_code_action" (ppSpace ident)* : attr
/--
Builtin command code action. See `command_code_action`.
-/
syntax (name := builtin_command_code_action) "builtin_command_code_action" (ppSpace ident)* : attr
/--
When `parent_dir` contains the current Lean file, `include_str "path" / "to" / "file"` becomes
a string literal with the contents of the file at `"parent_dir" / "path" / "to" / "file"`. If this
@@ -551,92 +529,3 @@ except that it doesn't print an empty diagnostic.
(This is effectively a synonym for `run_elab`.)
-/
syntax (name := runMeta) "run_meta " doSeq : command
/-- Element that can be part of a `#guard_msgs` specification. -/
syntax guardMsgsSpecElt := &"drop"? (&"info" <|> &"warning" <|> &"error" <|> &"all")
/-- Specification for `#guard_msgs` command. -/
syntax guardMsgsSpec := "(" guardMsgsSpecElt,* ")"
/--
`#guard_msgs` captures the messages generated by another command and checks that they
match the contents of the docstring attached to the `#guard_msgs` command.
Basic example:
```lean
/--
error: unknown identifier 'x'
-/
#guard_msgs in
example : α := x
```
This checks that there is such an error and then consumes the message entirely.
By default, the command intercepts all messages, but there is a way to specify which types
of messages to consider. For example, we can select only warnings:
```lean
/--
warning: declaration uses 'sorry'
-/
#guard_msgs(warning) in
example : α := sorry
```
or only errors
```lean
#guard_msgs(error) in
example : α := sorry
```
In this last example, since the message is not intercepted there is a warning on `sorry`.
We can drop the warning completely with
```lean
#guard_msgs(error, drop warning) in
example : α := sorry
```
Syntax description:
```
#guard_msgs (drop? info|warning|error|all,*)? in cmd
```
If there is no specification, `#guard_msgs` intercepts all messages.
Otherwise, if there is one, the specification is considered in left-to-right order, and the first
that applies chooses the outcome of the message:
- `info`, `warning`, `error`: intercept a message with the given severity level.
- `all`: intercept any message (so `#guard_msgs in cmd` and `#guard_msgs (all) in cmd`
are equivalent).
- `drop info`, `drop warning`, `drop error`: intercept a message with the given severity
level and then drop it. These messages are not checked.
- `drop all`: intercept a message and drop it.
For example, `#guard_msgs (error, drop all) in cmd` means to check warnings and then drop
everything else.
-/
syntax (name := guardMsgsCmd)
(docComment)? "#guard_msgs" (ppSpace guardMsgsSpec)? " in" ppLine command : command
namespace Parser
/--
`#check_tactic t ~> r by commands` runs the tactic sequence `commands`
on a goal with `t` and sees if the resulting expression has reduced it
to `r`.
-/
syntax (name := checkTactic) "#check_tactic " term "~>" term "by" tactic : command
/--
`#check_tactic_failure t by tac` runs the tactic `tac`
on a goal with `t` and verifies it fails.
-/
syntax (name := checkTacticFailure) "#check_tactic_failure " term "by" tactic : command
/--
`#check_simp t ~> r` checks `simp` reduces `t` to `r`.
-/
syntax (name := checkSimp) "#check_simp " term "~>" term : command
/--
`#check_simp t !~>` checks `simp` fails on reducing `t`.
-/
syntax (name := checkSimpFailure) "#check_simp " term "!~>" : command
end Parser

View File

@@ -170,6 +170,19 @@ See [Theorem Proving in Lean 4][tpil4] for more information.
-/
syntax (name := calcTactic) "calc" calcSteps : tactic
/--
Denotes a term that was omitted by the pretty printer.
This is only used for pretty printing, and it cannot be elaborated.
The presence of `⋯` is controlled by the `pp.deepTerms` and `pp.proofs` options.
-/
syntax "" : term
macro_rules | `() => Macro.throwError "\
Error: The '⋯' token is used by the pretty printer to indicate omitted terms, \
and it cannot be elaborated.\
\n\nIts presence in pretty printing output is controlled by the 'pp.deepTerms' and `pp.proofs` options. \
These options can be further adjusted using `pp.deepTerms.threshold` and `pp.proofs.threshold`."
@[app_unexpander Unit.unit] def unexpandUnit : Lean.PrettyPrinter.Unexpander
| `($(_)) => `(())
@@ -447,25 +460,3 @@ macro:50 e:term:51 " matches " p:sepBy1(term:51, " | ") : term =>
`(((match $e:term with | $[$p:term]|* => true | _ => false) : Bool))
end Lean
syntax "{" term,+ "}" : term
macro_rules
| `({$x:term}) => `(singleton $x)
| `({$x:term, $xs:term,*}) => `(insert $x {$xs:term,*})
namespace Lean
/-- Unexpander for the `{ x }` notation. -/
@[app_unexpander singleton]
def singletonUnexpander : Lean.PrettyPrinter.Unexpander
| `($_ $a) => `({ $a:term })
| _ => throw ()
/-- Unexpander for the `{ x, y, ... }` notation. -/
@[app_unexpander insert]
def insertUnexpander : Lean.PrettyPrinter.Unexpander
| `($_ $a { $ts:term,* }) => `({$a:term, $ts,*})
| _ => throw ()
end Lean

View File

@@ -1,8 +1,3 @@
/-
Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Omega.Int
import Init.Omega.IntList

View File

@@ -20,7 +20,7 @@ There is an equivalent file setting up `Coeffs` as a type synonym for `AssocList
currently in a private branch.
Not all the theorems about the algebraic operations on that representation have been proved yet.
When they are ready, we can replace the implementation in `omega` simply by importing
`Init.Omega.IntDict` instead of `Init.Omega.IntList`.
`Std.Tactic.Omega.Coeffs.IntDict` instead of `Std.Tactic.Omega.Coeffs.IntList`.
For small problems, the sparse representation is actually slightly slower,
so it is not urgent to make this replacement.

View File

@@ -5,14 +5,12 @@ Authors: Scott Morrison
-/
prelude
import Init.Data.Int.Order
import Init.Data.Int.DivModLemmas
import Init.Data.Nat.Lemmas
/-!
# Lemmas about `Nat`, `Int`, and `Fin` needed internally by `omega`.
# Lemmas about `Nat` and `Int` needed internally by `omega`.
These statements are useful for constructing proof expressions,
but unlikely to be widely useful, so are inside the `Lean.Omega` namespace.
but unlikely to be widely useful, so are inside the `Std.Tactic.Omega` namespace.
If you do find a use for them, please move them into the appropriate file and namespace!
-/
@@ -45,12 +43,6 @@ theorem ofNat_lt_of_lt {x y : Nat} (h : x < y) : (x : Int) < (y : Int) :=
theorem ofNat_le_of_le {x y : Nat} (h : x y) : (x : Int) (y : Int) :=
Int.ofNat_le.mpr h
theorem ofNat_shiftLeft_eq {x y : Nat} : (x <<< y : Int) = (x : Int) * (2 ^ y : Nat) := by
simp [Nat.shiftLeft_eq]
theorem ofNat_shiftRight_eq_div_pow {x y : Nat} : (x >>> y : Int) = (x : Int) / (2 ^ y : Nat) := by
simp [Nat.shiftRight_eq_div_pow]
-- FIXME these are insane:
theorem lt_of_not_ge {x y : Int} (h : ¬ (x y)) : y < x := Int.not_le.mp h
theorem lt_of_not_le {x y : Int} (h : ¬ (x y)) : y < x := Int.not_le.mp h
@@ -163,38 +155,6 @@ theorem le_of_ge {x y : Nat} (h : x ≥ y) : y ≤ x := ge_iff_le.mp h
end Nat
namespace Fin
theorem ne_iff_lt_or_gt {i j : Fin n} : i j i < j i > j := by
cases i; cases j; simp only [ne_eq, Fin.mk.injEq, Nat.ne_iff_lt_or_gt, gt_iff_lt]; rfl
protected theorem lt_or_gt_of_ne {i j : Fin n} (h : i j) : i < j i > j := Fin.ne_iff_lt_or_gt.mp h
theorem not_le {i j : Fin n} : ¬ i j j < i := by
cases i; cases j; exact Nat.not_le
theorem not_lt {i j : Fin n} : ¬ i < j j i := by
cases i; cases j; exact Nat.not_lt
protected theorem lt_of_not_le {i j : Fin n} (h : ¬ i j) : j < i := Fin.not_le.mp h
protected theorem le_of_not_lt {i j : Fin n} (h : ¬ i < j) : j i := Fin.not_lt.mp h
theorem ofNat_val_add {x y : Fin n} :
(((x + y : Fin n)) : Int) = ((x : Int) + (y : Int)) % n := rfl
theorem ofNat_val_sub {x y : Fin n} :
(((x - y : Fin n)) : Int) = ((x : Int) + ((n - y : Nat) : Int)) % n := rfl
theorem ofNat_val_mul {x y : Fin n} :
(((x * y : Fin n)) : Int) = ((x : Int) * (y : Int)) % n := rfl
theorem ofNat_val_natCast {n x y : Nat} (h : y = x % (n + 1)):
@Nat.cast Int instNatCastInt (@Fin.val (n + 1) (OfNat.ofNat x)) = OfNat.ofNat y := by
rw [h]
rfl
end Fin
namespace Prod
theorem of_lex (w : Prod.Lex r s p q) : r p.fst q.fst p.fst = q.fst s p.snd q.snd :=

View File

@@ -9,7 +9,7 @@ import Init.PropLemmas
# Specializations of basic logic lemmas
These are useful for `omega` while constructing proofs, but not considered generally useful
so are hidden in the `Lean.Omega` namespace.
so are hidden in the `Std.Tactic.Omega` namespace.
If you find yourself needing them elsewhere, please move them first to another file.
-/

View File

@@ -947,8 +947,7 @@ return `t` or `e` depending on whether `c` is true or false. The explicit argume
determines how to evaluate `c` to true or false. Write `if h : c then t else e`
instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact
that `c` is true/false.
-/
/-
Because Lean uses a strict (call-by-value) evaluation strategy, the signature of this
function is problematic in that it would require `t` and `e` to be evaluated before
calling the `ite` function, which would cause both sides of the `if` to be evaluated.
@@ -1815,8 +1814,6 @@ structure Fin (n : Nat) where
/-- If `i : Fin n`, then `i.2` is a proof that `i.1 < n`. -/
isLt : LT.lt val n
attribute [coe] Fin.val
theorem Fin.eq_of_val_eq {n} : {i j : Fin n}, Eq i.val j.val Eq i j
| _, _, _, _, rfl => rfl
@@ -2381,9 +2378,6 @@ Codepoint positions (counting the Unicode codepoints rather than bytes)
are represented by plain `Nat`s instead.
Indexing a `String` by a byte position is constant-time, while codepoint
positions need to be translated internally to byte positions in linear-time.
A byte position `p` is *valid* for a string `s` if `0 ≤ p ≤ s.endPos` and `p`
lies on a UTF8 byte boundary.
-/
structure String.Pos where
/-- Get the underlying byte index of a `String.Pos` -/

View File

@@ -1,5 +1,5 @@
/-
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Jeremy Avigad, Floris van Doorn, Mario Carneiro

View File

@@ -84,7 +84,6 @@ theorem dite_congr {_ : Decidable b} [Decidable c]
| inr h => rw [dif_neg h]; subst b; rw [dif_neg h]; exact h₃ h
@[simp] theorem ne_eq (a b : α) : (a b) = ¬(a = b) := rfl
norm_cast_add_elim ne_eq
@[simp] theorem ite_true (a b : α) : (if True then a else b) = a := rfl
@[simp] theorem ite_false (a b : α) : (if False then a else b) = b := rfl
@[simp] theorem dite_true {α : Sort u} {t : True α} {e : ¬ True α} : (dite True t e) = t True.intro := rfl

View File

@@ -5,7 +5,6 @@ Authors: Chris Lovett
-/
prelude
import Init.Data.String.Extra
import Init.Data.Nat.Linear
import Init.System.FilePath
namespace System

View File

@@ -584,43 +584,6 @@ def dsimpArg := simpErase.binary `orelse simpLemma
/-- A dsimp args list is a list of `dsimpArg`. This is the main argument to `dsimp`. -/
syntax dsimpArgs := " [" dsimpArg,* "]"
/-- The common arguments of `simp?` and `simp?!`. -/
syntax simpTraceArgsRest := (config)? (discharger)? (&" only")? (simpArgs)? (ppSpace location)?
/--
`simp?` takes the same arguments as `simp`, but reports an equivalent call to `simp only`
that would be sufficient to close the goal. This is useful for reducing the size of the simp
set in a local invocation to speed up processing.
```
example (x : Nat) : (if True then x + 2 else 3) = x + 2 := by
simp? -- prints "Try this: simp only [ite_true]"
```
This command can also be used in `simp_all` and `dsimp`.
-/
syntax (name := simpTrace) "simp?" "!"? simpTraceArgsRest : tactic
@[inherit_doc simpTrace]
macro tk:"simp?!" rest:simpTraceArgsRest : tactic => `(tactic| simp?%$tk ! $rest)
/-- The common arguments of `simp_all?` and `simp_all?!`. -/
syntax simpAllTraceArgsRest := (config)? (discharger)? (&" only")? (dsimpArgs)?
@[inherit_doc simpTrace]
syntax (name := simpAllTrace) "simp_all?" "!"? simpAllTraceArgsRest : tactic
@[inherit_doc simpTrace]
macro tk:"simp_all?!" rest:simpAllTraceArgsRest : tactic => `(tactic| simp_all?%$tk ! $rest)
/-- The common arguments of `dsimp?` and `dsimp?!`. -/
syntax dsimpTraceArgsRest := (config)? (&" only")? (dsimpArgs)? (ppSpace location)?
@[inherit_doc simpTrace]
syntax (name := dsimpTrace) "dsimp?" "!"? dsimpTraceArgsRest : tactic
@[inherit_doc simpTrace]
macro tk:"dsimp?!" rest:dsimpTraceArgsRest : tactic => `(tactic| dsimp?%$tk ! $rest)
/-- The arguments to the `simpa` family tactics. -/
syntax simpaArgsRest := (config)? (discharger)? &" only "? (simpArgs)? (" using " term)?
@@ -1055,6 +1018,7 @@ macro "haveI" d:haveDecl : tactic => `(tactic| refine_lift haveI $d:haveDecl; ?_
/-- `letI` behaves like `let`, but inlines the value instead of producing a `let_fun` term. -/
macro "letI" d:haveDecl : tactic => `(tactic| refine_lift letI $d:haveDecl; ?_)
/--
The `omega` tactic, for resolving integer and natural linear arithmetic problems.
@@ -1088,244 +1052,6 @@ Currently, all of these are on by default.
-/
syntax (name := omega) "omega" (config)? : tactic
/--
`bv_omega` is `omega` with an additional preprocessor that turns statements about `BitVec` into statements about `Nat`.
Currently the preprocessor is implemented as `try simp only [bv_toNat] at *`.
`bv_toNat` is a `@[simp]` attribute that you can (cautiously) add to more theorems.
-/
macro "bv_omega" : tactic => `(tactic| (try simp only [bv_toNat] at *) <;> omega)
/-- Implementation of `norm_cast` (the full `norm_cast` calls `trivial` afterwards). -/
syntax (name := normCast0) "norm_cast0" (location)? : tactic
/-- `assumption_mod_cast` is a variant of `assumption` that solves the goal
using a hypothesis. Unlike `assumption`, it first pre-processes the goal and
each hypothesis to move casts as far outwards as possible, so it can be used
in more situations.
Concretely, it runs `norm_cast` on the goal. For each local hypothesis `h`, it also
normalizes `h` with `norm_cast` and tries to use that to close the goal. -/
macro "assumption_mod_cast" : tactic => `(tactic| norm_cast0 at * <;> assumption)
/--
The `norm_cast` family of tactics is used to normalize casts inside expressions.
It is basically a `simp` tactic with a specific set of lemmas to move casts
upwards in the expression.
Therefore even in situations where non-terminal `simp` calls are discouraged (because of fragility),
`norm_cast` is considered safe.
It also has special handling of numerals.
For instance, given an assumption
```lean
a b :
h : ↑a + ↑b < (10 : )
```
writing `norm_cast at h` will turn `h` into
```lean
h : a + b < 10
```
There are also variants of `exact`, `apply`, `rw`, and `assumption` that
work modulo `norm_cast` - in other words, they apply `norm_cast` to make
them more flexible. They are called `exact_mod_cast`, `apply_mod_cast`,
`rw_mod_cast`, and `assumption_mod_cast`, respectively.
Writing `exact_mod_cast h` and `apply_mod_cast h` will normalize casts
in the goal and `h` before using `exact h` or `apply h`.
Writing `assumption_mod_cast` will normalize casts in the goal and, for
every hypothesis `h` in the context, it will try to normalize casts in `h` and use
`exact h`.
`rw_mod_cast` acts like the `rw` tactic but it applies `norm_cast` between steps.
See also `push_cast`, which moves casts inwards rather than lifting them outwards.
-/
macro "norm_cast" loc:(location)? : tactic =>
`(tactic| norm_cast0 $[$loc]? <;> try trivial)
/--
`push_cast` rewrites the goal to move casts inward, toward the leaf nodes.
This uses `norm_cast` lemmas in the forward direction.
For example, `↑(a + b)` will be written to `↑a + ↑b`.
It is equivalent to `simp only with push_cast`.
It can also be used at hypotheses with `push_cast at h`
and with extra simp lemmas with `push_cast [int.add_zero]`.
```lean
example (a b : ) (h1 : ((a + b : ) : ) = 10) (h2 : ((a + b + 0 : ) : ) = 10) :
((a + b : ) : ) = 10 :=
begin
push_cast,
push_cast at h1,
push_cast [int.add_zero] at h2,
end
```
-/
syntax (name := pushCast) "push_cast" (config)? (discharger)? (&" only")?
(" [" (simpStar <|> simpErase <|> simpLemma),* "]")? (location)? : tactic
/--
`norm_cast_add_elim foo` registers `foo` as an elim-lemma in `norm_cast`.
-/
syntax (name := normCastAddElim) "norm_cast_add_elim" ident : command
/--
* `symm` applies to a goal whose target has the form `t ~ u` where `~` is a symmetric relation,
that is, a relation which has a symmetry lemma tagged with the attribute [symm].
It replaces the target with `u ~ t`.
* `symm at h` will rewrite a hypothesis `h : t ~ u` to `h : u ~ t`.
-/
syntax (name := symm) "symm" (location)? : tactic
/-- For every hypothesis `h : a ~ b` where a `@[symm]` lemma is available,
add a hypothesis `h_symm : b ~ a`. -/
syntax (name := symmSaturate) "symm_saturate" : tactic
namespace SolveByElim
/-- Syntax for omitting a local hypothesis in `solve_by_elim`. -/
syntax erase := "-" term:max
/-- Syntax for including all local hypotheses in `solve_by_elim`. -/
syntax star := "*"
/-- Syntax for adding or removing a term, or `*`, in `solve_by_elim`. -/
syntax arg := star <|> erase <|> term
/-- Syntax for adding and removing terms in `solve_by_elim`. -/
syntax args := " [" SolveByElim.arg,* "]"
/-- Syntax for using all lemmas labelled with an attribute in `solve_by_elim`. -/
syntax using_ := " using " ident,*
end SolveByElim
section SolveByElim
open SolveByElim (args using_)
/--
`solve_by_elim` calls `apply` on the main goal to find an assumption whose head matches
and then repeatedly calls `apply` on the generated subgoals until no subgoals remain,
performing at most `maxDepth` (defaults to 6) recursive steps.
`solve_by_elim` discharges the current goal or fails.
`solve_by_elim` performs backtracking if subgoals can not be solved.
By default, the assumptions passed to `apply` are the local context, `rfl`, `trivial`,
`congrFun` and `congrArg`.
The assumptions can be modified with similar syntax as for `simp`:
* `solve_by_elim [h₁, h₂, ..., hᵣ]` also applies the given expressions.
* `solve_by_elim only [h₁, h₂, ..., hᵣ]` does not include the local context,
`rfl`, `trivial`, `congrFun`, or `congrArg` unless they are explicitly included.
* `solve_by_elim [-h₁, ... -hₙ]` removes the given local hypotheses.
* `solve_by_elim using [a₁, ...]` uses all lemmas which have been labelled
with the attributes `aᵢ` (these attributes must be created using `register_label_attr`).
`solve_by_elim*` tries to solve all goals together, using backtracking if a solution for one goal
makes other goals impossible.
(Adding or removing local hypotheses may not be well-behaved when starting with multiple goals.)
Optional arguments passed via a configuration argument as `solve_by_elim (config := { ... })`
- `maxDepth`: number of attempts at discharging generated subgoals
- `symm`: adds all hypotheses derived by `symm` (defaults to `true`).
- `exfalso`: allow calling `exfalso` and trying again if `solve_by_elim` fails
(defaults to `true`).
- `transparency`: change the transparency mode when calling `apply`. Defaults to `.default`,
but it is often useful to change to `.reducible`,
so semireducible definitions will not be unfolded when trying to apply a lemma.
See also the doc-comment for `Std.Tactic.BacktrackConfig` for the options
`proc`, `suspend`, and `discharge` which allow further customization of `solve_by_elim`.
Both `apply_assumption` and `apply_rules` are implemented via these hooks.
-/
syntax (name := solveByElim)
"solve_by_elim" "*"? (config)? (&" only")? (args)? (using_)? : tactic
/--
`apply_assumption` looks for an assumption of the form `... → ∀ _, ... → head`
where `head` matches the current goal.
You can specify additional rules to apply using `apply_assumption [...]`.
By default `apply_assumption` will also try `rfl`, `trivial`, `congrFun`, and `congrArg`.
If you don't want these, or don't want to use all hypotheses, use `apply_assumption only [...]`.
You can use `apply_assumption [-h]` to omit a local hypothesis.
You can use `apply_assumption using [a₁, ...]` to use all lemmas which have been labelled
with the attributes `aᵢ` (these attributes must be created using `register_label_attr`).
`apply_assumption` will use consequences of local hypotheses obtained via `symm`.
If `apply_assumption` fails, it will call `exfalso` and try again.
Thus if there is an assumption of the form `P → ¬ Q`, the new tactic state
will have two goals, `P` and `Q`.
You can pass a further configuration via the syntax `apply_rules (config := {...}) lemmas`.
The options supported are the same as for `solve_by_elim` (and include all the options for `apply`).
-/
syntax (name := applyAssumption)
"apply_assumption" (config)? (&" only")? (args)? (using_)? : tactic
/--
`apply_rules [l₁, l₂, ...]` tries to solve the main goal by iteratively
applying the list of lemmas `[l₁, l₂, ...]` or by applying a local hypothesis.
If `apply` generates new goals, `apply_rules` iteratively tries to solve those goals.
You can use `apply_rules [-h]` to omit a local hypothesis.
`apply_rules` will also use `rfl`, `trivial`, `congrFun` and `congrArg`.
These can be disabled, as can local hypotheses, by using `apply_rules only [...]`.
You can use `apply_rules using [a₁, ...]` to use all lemmas which have been labelled
with the attributes `aᵢ` (these attributes must be created using `register_label_attr`).
You can pass a further configuration via the syntax `apply_rules (config := {...})`.
The options supported are the same as for `solve_by_elim` (and include all the options for `apply`).
`apply_rules` will try calling `symm` on hypotheses and `exfalso` on the goal as needed.
This can be disabled with `apply_rules (config := {symm := false, exfalso := false})`.
You can bound the iteration depth using the syntax `apply_rules (config := {maxDepth := n})`.
Unlike `solve_by_elim`, `apply_rules` does not perform backtracking, and greedily applies
a lemma from the list until it gets stuck.
-/
syntax (name := applyRules) "apply_rules" (config)? (&" only")? (args)? (using_)? : tactic
end SolveByElim
/--
Searches environment for definitions or theorems that can solve the goal using `exact`
with conditions resolved by `solve_by_elim`.
The optional `using` clause provides identifiers in the local context that must be
used by `exact?` when closing the goal. This is most useful if there are multiple
ways to resolve the goal, and one wants to guide which lemma is used.
-/
syntax (name := exact?) "exact?" (" using " (colGt ident),+)? : tactic
/--
Searches environment for definitions or theorems that can refine the goal using `apply`
with conditions resolved when possible with `solve_by_elim`.
The optional `using` clause provides identifiers in the local context that must be
used when closing the goal.
-/
syntax (name := apply?) "apply?" (" using " (colGt term),+)? : tactic
/--
`show_term tac` runs `tac`, then prints the generated term in the form
"exact X Y Z" or "refine X ?_ Z" if there are remaining subgoals.
(For some tactics, the printed term will not be human readable.)
-/
syntax (name := showTerm) "show_term " tacticSeq : tactic
/--
`show_term e` elaborates `e`, then prints the generated term.
-/
macro (name := showTermElab) tk:"show_term " t:term : term =>
`(term| no_implicit_lambda% (show_term_elab%$tk $t))
/--
The command `by?` will print a suggestion for replacing the proof block with a proof term
using `show_term`.
-/
macro (name := by?) tk:"by?" t:tacticSeq : term => `(show_term%$tk by%$tk $t)
end Tactic
namespace Attr
@@ -1373,59 +1099,6 @@ If there are several with the same priority, it is uses the "most recent one". E
```
-/
syntax (name := simp) "simp" (Tactic.simpPre <|> Tactic.simpPost)? (ppSpace prio)? : attr
/-- The possible `norm_cast` kinds: `elim`, `move`, or `squash`. -/
syntax normCastLabel := &"elim" <|> &"move" <|> &"squash"
/--
The `norm_cast` attribute should be given to lemmas that describe the
behaviour of a coercion with respect to an operator, a relation, or a particular
function.
It only concerns equality or iff lemmas involving `↑`, `⇑` and `↥`, describing the behavior of
the coercion functions.
It does not apply to the explicit functions that define the coercions.
Examples:
```lean
@[norm_cast] theorem coe_nat_inj' {m n : } : (↑m : ) = ↑n ↔ m = n
@[norm_cast] theorem coe_int_denom (n : ) : (n : ).denom = 1
@[norm_cast] theorem cast_id : ∀ n : , ↑n = n
@[norm_cast] theorem coe_nat_add (m n : ) : (↑(m + n) : ) = ↑m + ↑n
@[norm_cast] theorem cast_coe_nat (n : ) : ((n : ) : α) = n
@[norm_cast] theorem cast_one : ((1 : ) : α) = 1
```
Lemmas tagged with `@[norm_cast]` are classified into three categories: `move`, `elim`, and
`squash`. They are classified roughly as follows:
* elim lemma: LHS has 0 head coes and ≥ 1 internal coe
* move lemma: LHS has 1 head coe and 0 internal coes, RHS has 0 head coes and ≥ 1 internal coes
* squash lemma: LHS has ≥ 1 head coes and 0 internal coes, RHS has fewer head coes
`norm_cast` uses `move` and `elim` lemmas to factor coercions toward the root of an expression
and to cancel them from both sides of an equation or relation. It uses `squash` lemmas to clean
up the result.
It is typically not necessary to specify these categories, as `norm_cast` lemmas are
automatically classified by default. The automatic classification can be overridden by
giving an optional `elim`, `move`, or `squash` parameter to the attribute.
```lean
@[simp, norm_cast elim] lemma nat_cast_re (n : ) : (n : ).re = n := by
rw [← of_real_nat_cast, of_real_re]
```
Don't do this unless you understand what you are doing.
-/
syntax (name := norm_cast) "norm_cast" (ppSpace normCastLabel)? (ppSpace num)? : attr
end Attr
end Parser
@@ -1445,14 +1118,13 @@ macro_rules | `($type) => `((by assumption : $type))
by the notation `arr[i]` to prove any side conditions that arise when
constructing the term (e.g. the index is in bounds of the array).
The default behavior is to just try `trivial` (which handles the case
where `i < arr.size` is in the context) and `simp_arith` and `omega`
where `i < arr.size` is in the context) and `simp_arith`
(for doing linear arithmetic in the index).
-/
syntax "get_elem_tactic_trivial" : tactic
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| omega)
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| simp (config := { arith := true }); done)
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| trivial)
macro_rules | `(tactic| get_elem_tactic_trivial) => `(tactic| simp (config := { arith := true }); done)
/--
`get_elem_tactic` is the tactic automatically called by the notation `arr[i]`
@@ -1463,24 +1135,6 @@ users are encouraged to extend `get_elem_tactic_trivial` instead of this tactic.
-/
macro "get_elem_tactic" : tactic =>
`(tactic| first
/-
Recall that `macro_rules` are tried in reverse order.
We want `assumption` to be tried first.
This is important for theorems such as
```
[simp] theorem getElem_pop (a : Array α) (i : Nat) (hi : i < a.pop.size) :
a.pop[i] = a[i]'(Nat.lt_of_lt_of_le (a.size_pop ▸ hi) (Nat.sub_le _ _)) :=
```
There is a proof embedded in the right-hand-side, and we want it to be just `hi`.
If `omega` is used to "fill" this proof, we will have a more complex proof term that
cannot be inferred by unification.
We hardcoded `assumption` here to ensure users cannot accidentaly break this IF
they add new `macro_rules` for `get_elem_tactic_trivial`.
TODO: Implement priorities for `macro_rules`.
TODO: Ensure we have a **high-priority** macro_rules for `get_elem_tactic_trivial` which is just `assumption`.
-/
| assumption
| get_elem_tactic_trivial
| fail "failed to prove index is valid, possible solutions:
- Use `have`-expressions to prove the index is valid
@@ -1496,9 +1150,3 @@ macro_rules | `($x[$i]) => `(getElem $x $i (by get_elem_tactic))
@[inherit_doc getElem]
syntax term noWs "[" withoutPosition(term) "]'" term:max : term
macro_rules | `($x[$i]'$h) => `(getElem $x $i $h)
/--
Searches environment for definitions or theorems that can be substituted in
for `exact?% to solve the goal.
-/
syntax (name := Lean.Parser.Syntax.exact?) "exact?%" : term

View File

@@ -63,24 +63,4 @@ macro_rules
| 0 => `(tactic| skip)
| n+1 => `(tactic| ($seq:tacticSeq); iterate $(quote n) $seq:tacticSeq)
/--
Rewrites with the given rules, normalizing casts prior to each step.
-/
syntax "rw_mod_cast" (config)? rwRuleSeq (location)? : tactic
macro_rules
| `(tactic| rw_mod_cast $[$config]? [$rules,*] $[$loc]?) => do
let tacs rules.getElems.mapM fun rule =>
`(tactic| (norm_cast at *; rw $[$config]? [$rule] $[$loc]?))
`(tactic| ($[$tacs]*))
/--
Normalize casts in the goal and the given expression, then close the goal with `exact`.
-/
macro "exact_mod_cast " e:term : tactic => `(tactic| exact mod_cast ($e : _))
/--
Normalize casts in the goal and the given expression, then `apply` the expression to the goal.
-/
macro "apply_mod_cast " e:term : tactic => `(tactic| apply mod_cast ($e : _))
end Lean.Parser.Tactic

View File

@@ -22,8 +22,7 @@ macro_rules | `(tactic| decreasing_trivial) => `(tactic| linarith)
-/
syntax "decreasing_trivial" : tactic
macro_rules | `(tactic| decreasing_trivial) => `(tactic| (simp (config := { arith := true, failIfUnchanged := false })) <;> done)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| omega)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| (simp (config := { arith := true, failIfUnchanged := false })); done)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| assumption)
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply Nat.sub_succ_lt_self; assumption) -- a - (i+1) < a - i if i < a
macro_rules | `(tactic| decreasing_trivial) => `(tactic| apply Nat.pred_lt'; assumption) -- i-1 < i if j < i

View File

@@ -35,4 +35,3 @@ import Lean.Widget
import Lean.Log
import Lean.Linter
import Lean.SubExpr
import Lean.LabelAttribute

View File

@@ -5,7 +5,6 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.ProjFns
import Lean.Meta.CtorRecognizer
import Lean.Compiler.BorrowedAnnotation
import Lean.Compiler.LCNF.Types
import Lean.Compiler.LCNF.Bind
@@ -620,7 +619,7 @@ where
let rhs liftMetaM do Meta.whnf args[inductVal.numParams + inductVal.numIndices + 2]!
let lhs := lhs.toCtorIfLit
let rhs := rhs.toCtorIfLit
match ( liftMetaM <| Meta.isConstructorApp? lhs), ( liftMetaM <| Meta.isConstructorApp? rhs) with
match lhs.isConstructorApp? ( getEnv), rhs.isConstructorApp? ( getEnv) with
| some lhsCtorVal, some rhsCtorVal =>
if lhsCtorVal.name == rhsCtorVal.name then
etaIfUnderApplied e (arity+1) do

View File

@@ -8,7 +8,6 @@ prelude
import Init.Data.List.Control
import Init.Data.Range
import Init.Data.OfScientific
import Init.Data.Hashable
import Lean.Data.RBMap
namespace Lean
@@ -16,7 +15,7 @@ namespace Lean
structure JsonNumber where
mantissa : Int
exponent : Nat
deriving DecidableEq, Hashable
deriving DecidableEq
namespace JsonNumber
@@ -206,19 +205,6 @@ private partial def beq' : Json → Json → Bool
instance : BEq Json where
beq := beq'
private partial def hash' : Json UInt64
| null => 11
| bool b => mixHash 13 <| hash b
| num n => mixHash 17 <| hash n
| str s => mixHash 19 <| hash s
| arr elems =>
mixHash 23 <| elems.foldl (init := 7) fun r a => mixHash r (hash' a)
| obj kvPairs =>
mixHash 29 <| kvPairs.fold (init := 7) fun r k v => mixHash r <| mixHash (hash k) (hash' v)
instance : Hashable Json where
hash := hash'
-- HACK(Marc): temporary ugliness until we can use RBMap for JSON objects
def mkObj (o : List (String × Json)) : Json :=
obj <| Id.run do

View File

@@ -1,7 +1,7 @@
/-
Copyright (c) 2022 E.W.Ayers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: E.W.Ayers, Wojciech Nawrocki
Copyright (c) 2022 E.W.Ayers. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: E.W.Ayers, Wojciech Nawrocki
-/
prelude
import Lean.Data.Json.FromToJson

View File

@@ -47,19 +47,19 @@ structure CompletionItem where
documentation? : Option MarkupContent := none
kind? : Option CompletionItemKind := none
textEdit? : Option InsertReplaceEdit := none
sortText? : Option String := none
data? : Option Json := none
/-
tags? : CompletionItemTag[]
deprecated? : boolean
preselect? : boolean
sortText? : string
filterText? : string
insertText? : string
insertTextFormat? : InsertTextFormat
insertTextMode? : InsertTextMode
additionalTextEdits? : TextEdit[]
commitCharacters? : string[]
command? : Command -/
command? : Command
data? : any -/
deriving FromJson, ToJson, Inhabited
structure CompletionList where
@@ -274,7 +274,7 @@ structure CallHierarchyItem where
uri : DocumentUri
range : Range
selectionRange : Range
data? : Option Json := none
-- data? : Option unknown
deriving FromJson, ToJson, BEq, Hashable, Inhabited
structure CallHierarchyIncomingCallsParams where

View File

@@ -86,10 +86,6 @@ def leanPosToLspPos (text : FileMap) : Lean.Position → Lsp.Position
def utf8PosToLspPos (text : FileMap) (pos : String.Pos) : Lsp.Position :=
text.leanPosToLspPos (text.toPosition pos)
/-- Gets the LSP range from a `String.Range`. -/
def utf8RangeToLspRange (text : FileMap) (range : String.Range) : Lsp.Range :=
{ start := text.utf8PosToLspPos range.start, «end» := text.utf8PosToLspPos range.stop }
end FileMap
end Lean

View File

@@ -5,8 +5,6 @@ Authors: Leonardo de Moura
-/
prelude
import Init.Data.Ord
import Init.Data.Nat.Linear
namespace Lean
universe u v w w'

View File

@@ -1,8 +1,3 @@
/-
Copyright (c) 2021 Daniel Fabian. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Daniel Fabian
-/
prelude
import Lean.Data.Xml.Basic
import Lean.Data.Xml.Parser

View File

@@ -1,3 +1,4 @@
/-
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
@@ -37,3 +38,4 @@ private partial def cToString : Content → String
end
instance : ToString Element := eToString
instance : ToString Content := cToString

View File

@@ -12,6 +12,42 @@ namespace Lean
private builtin_initialize builtinDocStrings : IO.Ref (NameMap String) IO.mkRef {}
private builtin_initialize docStringExt : MapDeclarationExtension String mkMapDeclarationExtension
private def findLeadingSpacesSize (s : String) : Nat :=
let it := s.iter
let it := it.find (· == '\n') |>.next
consumeSpaces it 0 s.length
where
consumeSpaces (it : String.Iterator) (curr min : Nat) : Nat :=
if it.atEnd then min
else if it.curr == ' ' || it.curr == '\t' then consumeSpaces it.next (curr + 1) min
else if it.curr == '\n' then findNextLine it.next min
else findNextLine it.next (Nat.min curr min)
findNextLine (it : String.Iterator) (min : Nat) : Nat :=
if it.atEnd then min
else if it.curr == '\n' then consumeSpaces it.next 0 min
else findNextLine it.next min
private def removeNumLeadingSpaces (n : Nat) (s : String) : String :=
consumeSpaces n s.iter ""
where
consumeSpaces (n : Nat) (it : String.Iterator) (r : String) : String :=
match n with
| 0 => saveLine it r
| n+1 =>
if it.atEnd then r
else if it.curr == ' ' || it.curr == '\t' then consumeSpaces n it.next r
else saveLine it r
termination_by (it, 1)
saveLine (it : String.Iterator) (r : String) : String :=
if it.atEnd then r
else if it.curr == '\n' then consumeSpaces n it.next (r.push '\n')
else saveLine it.next (r.push it.curr)
termination_by (it, 0)
def removeLeadingSpaces (s : String) : String :=
let n := findLeadingSpacesSize s
if n == 0 then s else removeNumLeadingSpaces n s
def addBuiltinDocString (declName : Name) (docString : String) : IO Unit :=
builtinDocStrings.modify (·.insert declName (removeLeadingSpaces docString))
@@ -55,4 +91,9 @@ def getDocStringText [Monad m] [MonadError m] [MonadRef m] (stx : TSyntax `Lean.
| Syntax.atom _ val => return val.extract 0 (val.endPos - 2)
| _ => throwErrorAt stx "unexpected doc string{indentD stx.raw[1]}"
def TSyntax.getDocString (stx : TSyntax `Lean.Parser.Command.docComment) : String :=
match stx.raw[1] with
| Syntax.atom _ val => val.extract 0 (val.endPos - 2)
| _ => ""
end Lean

View File

@@ -47,6 +47,3 @@ import Lean.Elab.Eval
import Lean.Elab.Calc
import Lean.Elab.InheritDoc
import Lean.Elab.ParseImportsFast
import Lean.Elab.GuardMsgs
import Lean.Elab.CheckTactic
import Lean.Elab.MatchExpr

View File

@@ -534,10 +534,10 @@ open Meta
def elabCheckCore (ignoreStuckTC : Bool) : CommandElab
| `(#check%$tk $term) => withoutModifyingEnv <| runTermElabM fun _ => Term.withDeclName `_check do
-- show signature for `#check id`/`#check @id`
if let `($id:ident) := term then
if let `($_:ident) := term then
try
for c in ( resolveGlobalConstWithInfos term) do
addCompletionInfo <| .id term id.getId (danglingDot := false) {} none
addCompletionInfo <| .id term c (danglingDot := false) {} none
logInfoAt tk <| .ofPPFormat { pp := fun
| some ctx => ctx.runMetaM <| PrettyPrinter.ppSignature c
| none => return f!"{c}" -- should never happen

View File

@@ -99,14 +99,6 @@ private def elabOptLevel (stx : Syntax) : TermElabM Level :=
else
throwError "synthetic hole has already been defined with an incompatible local context"
@[builtin_term_elab Lean.Parser.Term.omission] def elabOmission : TermElab := fun stx expectedType? => do
logWarning m!"\
The '⋯' token is used by the pretty printer to indicate omitted terms, and it should not be used directly. \
It logs this warning and then elaborates like `_`.\
\n\nThe presence of `⋯` in pretty printing output is controlled by the 'pp.deepTerms' and `pp.proofs` options. \
These options can be further adjusted using `pp.deepTerms.threshold` and `pp.proofs.threshold`."
elabHole stx expectedType?
@[builtin_term_elab «letMVar»] def elabLetMVar : TermElab := fun stx expectedType? => do
match stx with
| `(let_mvar% ? $n := $e; $b) =>
@@ -166,10 +158,7 @@ private def mkTacticMVar (type : Expr) (tacticCode : Syntax) : TermElabM Expr :=
@[builtin_term_elab noImplicitLambda] def elabNoImplicitLambda : TermElab := fun stx expectedType? =>
elabTerm stx[1] (mkNoImplicitLambdaAnnotation <$> expectedType?)
@[builtin_term_elab Lean.Parser.Term.cdot] def elabBadCDot : TermElab := fun stx expectedType? => do
if stx[0].getAtomVal == "." then
-- Users may input bad cdots because they are trying to auto-complete them using the expected type
addCompletionInfo <| CompletionInfo.dotId stx .anonymous ( getLCtx) expectedType?
@[builtin_term_elab Lean.Parser.Term.cdot] def elabBadCDot : TermElab := fun _ _ =>
throwError "invalid occurrence of `·` notation, it must be surrounded by parentheses (e.g. `(· + 1)`)"
@[builtin_term_elab str] def elabStrLit : TermElab := fun stx _ => do

View File

@@ -1,95 +0,0 @@
/-
Copyright (c) 2024 Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Joe Hendrix
-/
prelude
import Lean.Elab.Tactic.ElabTerm
import Lean.Elab.Command
import Lean.Elab.Tactic.Meta
/-!
Commands to validate tactic results.
-/
namespace Lean.Elab.CheckTactic
open Lean.Meta CheckTactic
open Lean.Elab.Tactic
open Lean.Elab.Command
private def matchCheckGoalType (stx : Syntax) (goalType : Expr) : MetaM (Expr × Expr × Level) := do
let u mkFreshLevelMVar
let type mkFreshExprMVar (some (.sort u))
let val mkFreshExprMVar (some type)
let extType := mkAppN (.const ``CheckGoalType [u]) #[type, val]
if !( isDefEq goalType extType) then
throwErrorAt stx "Goal{indentExpr goalType}\nis expected to match {indentExpr extType}"
pure (val, type, u)
@[builtin_command_elab Lean.Parser.checkTactic]
def elabCheckTactic : CommandElab := fun stx => do
let `(#check_tactic $t ~> $result by $tac) := stx | throwUnsupportedSyntax
withoutModifyingEnv $ do
runTermElabM $ fun _vars => do
let u Lean.Elab.Term.elabTerm t none
let type inferType u
let lvl mkFreshLevelMVar
let checkGoalType : Expr := mkApp2 (mkConst ``CheckGoalType [lvl]) type u
let mvar mkFreshExprMVar (.some checkGoalType)
let (goals, _) Lean.Elab.runTactic mvar.mvarId! tac.raw
let expTerm Lean.Elab.Term.elabTerm result (.some type)
match goals with
| [] =>
throwErrorAt stx
m!"{tac} closed goal, but is expected to reduce to {indentExpr expTerm}."
| [next] => do
let (val, _, _) matchCheckGoalType stx (next.getType)
if !( Meta.withReducible <| isDefEq val expTerm) then
throwErrorAt stx
m!"Term reduces to{indentExpr val}\nbut is expected to reduce to {indentExpr expTerm}"
| _ => do
throwErrorAt stx
m!"{tac} produced multiple goals, but is expected to reduce to {indentExpr expTerm}."
pure ()
@[builtin_command_elab Lean.Parser.checkTacticFailure]
def elabCheckTacticFailure : CommandElab := fun stx => do
let `(#check_tactic_failure $t by $tactic) := stx | throwUnsupportedSyntax
withoutModifyingEnv $ do
runTermElabM $ fun _vars => do
let val Lean.Elab.Term.elabTerm t none
let type inferType val
let lvl mkFreshLevelMVar
let checkGoalType : Expr := mkApp2 (mkConst ``CheckGoalType [lvl]) type val
let mvar mkFreshExprMVar (.some checkGoalType)
let act := Lean.Elab.runTactic mvar.mvarId! tactic.raw
match try (Term.withoutErrToSorry (some <$> act)) catch _ => pure none with
| none =>
pure ()
| some (gls, _) =>
let ppGoal (g : MVarId) := do
let (val, _type, _u) matchCheckGoalType stx ( g.getType)
pure m!"{indentExpr val}"
let msg
match gls with
| [] => pure m!"{tactic} expected to fail on {val}, but closed goal."
| [g] =>
pure <| m!"{tactic} expected to fail on {val}, but returned: {←ppGoal g}"
| gls =>
let app m g := do pure <| m ++ (ppGoal g)
let init := m!"{tactic} expected to fail on {val}, but returned goals:"
gls.foldlM (init := init) app
throwErrorAt stx msg
@[builtin_macro Lean.Parser.checkSimp]
def expandCheckSimp : Macro := fun stx => do
let `(#check_simp $t ~> $exp) := stx | Macro.throwUnsupported
`(command|#check_tactic $t ~> $exp by simp)
@[builtin_macro Lean.Parser.checkSimpFailure]
def expandCheckSimpFailure : Macro := fun stx => do
let `(#check_simp $t !~>) := stx | Macro.throwUnsupported
`(command|#check_tactic_failure $t by simp)
end Lean.Elab.CheckTactic

View File

@@ -347,21 +347,7 @@ def elabMutual : CommandElab := fun stx => do
let attrs elabAttrs attrInsts
let idents := stx[4].getArgs
for ident in idents do withRef ident <| liftTermElabM do
/-
HACK to allow `attribute` command to disable builtin simprocs.
TODO: find a better solution. Example: have some "fake" declaration
for builtin simprocs.
-/
let declNames
try
resolveGlobalConst ident
catch _ =>
let name := ident.getId.eraseMacroScopes
if ( Simp.isBuiltinSimproc name) then
pure [name]
else
throwUnknownConstant name
let declName ensureNonAmbiguous ident declNames
let declName resolveGlobalConstNoOverloadWithInfo ident
Term.applyAttributes declName attrs
for attrName in toErase do
Attribute.erase declName attrName

View File

@@ -131,31 +131,12 @@ abbrev Var := Syntax -- TODO: should be `Ident`
/-- A `doMatch` alternative. `vars` is the array of variables declared by `patterns`. -/
structure Alt (σ : Type) where
ref : Syntax
vars : Array Var
ref : Syntax
vars : Array Var
patterns : Syntax
rhs : σ
rhs : σ
deriving Inhabited
/-- A `doMatchExpr` alternative. -/
structure AltExpr (σ : Type) where
ref : Syntax
var? : Option Var
funName : Syntax
pvars : Array Syntax
rhs : σ
deriving Inhabited
def AltExpr.vars (alt : AltExpr σ) : Array Var := Id.run do
let mut vars := #[]
if let some var := alt.var? then
vars := vars.push var
for pvar in alt.pvars do
match pvar with
| `(_) => pure ()
| _ => vars := vars.push pvar
return vars
/--
Auxiliary datastructure for representing a `do` code block, and compiling "reassignments" (e.g., `x := x + 1`).
We convert `Code` into a `Syntax` term representing the:
@@ -217,7 +198,6 @@ inductive Code where
/-- Recall that an if-then-else may declare a variable using `optIdent` for the branches `thenBranch` and `elseBranch`. We store the variable name at `var?`. -/
| ite (ref : Syntax) (h? : Option Var) (optIdent : Syntax) (cond : Syntax) (thenBranch : Code) (elseBranch : Code)
| match (ref : Syntax) (gen : Syntax) (discrs : Syntax) (optMotive : Syntax) (alts : Array (Alt Code))
| matchExpr (ref : Syntax) (meta : Bool) (discr : Syntax) (alts : Array (AltExpr Code)) (elseBranch : Code)
| jmp (ref : Syntax) (jpName : Name) (args : Array Syntax)
deriving Inhabited
@@ -232,7 +212,6 @@ def Code.getRef? : Code → Option Syntax
| .return ref _ => ref
| .ite ref .. => ref
| .match ref .. => ref
| .matchExpr ref .. => ref
| .jmp ref .. => ref
abbrev VarSet := RBMap Name Syntax Name.cmp
@@ -264,28 +243,19 @@ partial def CodeBlocl.toMessageData (codeBlock : CodeBlock) : MessageData :=
| .match _ _ ds _ alts =>
m!"match {ds} with"
++ alts.foldl (init := m!"") fun acc alt => acc ++ m!"\n| {alt.patterns} => {loop alt.rhs}"
| .matchExpr _ meta d alts elseCode =>
let r := m!"match_expr {if meta then "" else "(meta := false)"} {d} with"
let r := r ++ alts.foldl (init := m!"") fun acc alt =>
let acc := acc ++ m!"\n| {if let some var := alt.var? then m!"{var}@" else ""}"
let acc := acc ++ m!"{alt.funName}"
let acc := acc ++ alt.pvars.foldl (init := m!"") fun acc pvar => acc ++ m!" {pvar}"
acc ++ m!" => {loop alt.rhs}"
r ++ m!"| _ => {loop elseCode}"
loop codeBlock.code
/-- Return true if the give code contains an exit point that satisfies `p` -/
partial def hasExitPointPred (c : Code) (p : Code Bool) : Bool :=
let rec loop : Code Bool
| .decl _ _ k => loop k
| .reassign _ _ k => loop k
| .joinpoint _ _ b k => loop b || loop k
| .seq _ k => loop k
| .ite _ _ _ _ t e => loop t || loop e
| .match _ _ _ _ alts => alts.any (loop ·.rhs)
| .matchExpr _ _ _ alts e => alts.any (loop ·.rhs) || loop e
| .jmp .. => false
| c => p c
| .decl _ _ k => loop k
| .reassign _ _ k => loop k
| .joinpoint _ _ b k => loop b || loop k
| .seq _ k => loop k
| .ite _ _ _ _ t e => loop t || loop e
| .match _ _ _ _ alts => alts.any (loop ·.rhs)
| .jmp .. => false
| c => p c
loop c
def hasExitPoint (c : Code) : Bool :=
@@ -330,18 +300,13 @@ partial def convertTerminalActionIntoJmp (code : Code) (jp : Name) (xs : Array V
| .joinpoint n ps b k => return .joinpoint n ps ( loop b) ( loop k)
| .seq e k => return .seq e ( loop k)
| .ite ref x? h c t e => return .ite ref x? h c ( loop t) ( loop e)
| .match ref g ds t alts => return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( loop alt.rhs) })
| .action e => mkAuxDeclFor e fun y =>
let ref := e
-- We jump to `jp` with xs **and** y
let jmpArgs := xs.push y
return Code.jmp ref jp jmpArgs
| .match ref g ds t alts =>
return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( loop alt.rhs) })
| .matchExpr ref meta d alts e => do
let alts alts.mapM fun alt => do pure { alt with rhs := ( loop alt.rhs) }
let e loop e
return .matchExpr ref meta d alts e
| c => return c
| c => return c
loop code
structure JPDecl where
@@ -407,13 +372,14 @@ def mkJmp (ref : Syntax) (rs : VarSet) (val : Syntax) (mkJPBody : Syntax → Mac
return Code.jmp ref jp args
/-- `pullExitPointsAux rs c` auxiliary method for `pullExitPoints`, `rs` is the set of update variable in the current path. -/
partial def pullExitPointsAux (rs : VarSet) (c : Code) : StateRefT (Array JPDecl) TermElabM Code := do
partial def pullExitPointsAux (rs : VarSet) (c : Code) : StateRefT (Array JPDecl) TermElabM Code :=
match c with
| .decl xs stx k => return .decl xs stx ( pullExitPointsAux (eraseVars rs xs) k)
| .reassign xs stx k => return .reassign xs stx ( pullExitPointsAux (insertVars rs xs) k)
| .joinpoint j ps b k => return .joinpoint j ps ( pullExitPointsAux rs b) ( pullExitPointsAux rs k)
| .seq e k => return .seq e ( pullExitPointsAux rs k)
| .ite ref x? o c t e => return .ite ref x? o c ( pullExitPointsAux (eraseOptVar rs x?) t) ( pullExitPointsAux (eraseOptVar rs x?) e)
| .match ref g ds t alts => return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) })
| .jmp .. => return c
| .break ref => mkSimpleJmp ref rs (.break ref)
| .continue ref => mkSimpleJmp ref rs (.continue ref)
@@ -423,13 +389,6 @@ partial def pullExitPointsAux (rs : VarSet) (c : Code) : StateRefT (Array JPDecl
mkAuxDeclFor e fun y =>
let ref := e
mkJmp ref rs y (fun yFresh => return .action ( ``(Pure.pure $yFresh)))
| .match ref g ds t alts =>
let alts alts.mapM fun alt => do pure { alt with rhs := ( pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) }
return .match ref g ds t alts
| .matchExpr ref meta d alts e =>
let alts alts.mapM fun alt => do pure { alt with rhs := ( pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) }
let e pullExitPointsAux rs e
return .matchExpr ref meta d alts e
/--
Auxiliary operation for adding new variables to the collection of updated variables in a CodeBlock.
@@ -498,14 +457,6 @@ partial def extendUpdatedVarsAux (c : Code) (ws : VarSet) : TermElabM Code :=
pullExitPoints c
else
return .match ref g ds t ( alts.mapM fun alt => do pure { alt with rhs := ( update alt.rhs) })
| .matchExpr ref meta d alts e =>
if alts.any fun alt => alt.vars.any fun x => ws.contains x.getId then
-- If a pattern variable is shadowing a variable in ws, we `pullExitPoints`
pullExitPoints c
else
let alts alts.mapM fun alt => do pure { alt with rhs := ( update alt.rhs) }
let e update e
return .matchExpr ref meta d alts e
| .ite ref none o c t e => return .ite ref none o c ( update t) ( update e)
| .ite ref (some h) o cond t e =>
if ws.contains h.getId then
@@ -619,16 +570,6 @@ def mkMatch (ref : Syntax) (genParam : Syntax) (discrs : Syntax) (optMotive : Sy
return { ref := alt.ref, vars := alt.vars, patterns := alt.patterns, rhs := rhs.code : Alt Code }
return { code := .match ref genParam discrs optMotive alts, uvars := ws }
def mkMatchExpr (ref : Syntax) (meta : Bool) (discr : Syntax) (alts : Array (AltExpr CodeBlock)) (elseBranch : CodeBlock) : TermElabM CodeBlock := do
-- nary version of homogenize
let ws := alts.foldl (union · ·.rhs.uvars) {}
let ws := union ws elseBranch.uvars
let alts alts.mapM fun alt => do
let rhs extendUpdatedVars alt.rhs ws
return { alt with rhs := rhs.code : AltExpr Code }
let elseBranch extendUpdatedVars elseBranch ws
return { code := .matchExpr ref meta discr alts elseBranch.code, uvars := ws }
/-- Return a code block that executes `terminal` and then `k` with the value produced by `terminal`.
This method assumes `terminal` is a terminal -/
def concat (terminal : CodeBlock) (kRef : Syntax) (y? : Option Var) (k : CodeBlock) : TermElabM CodeBlock := do
@@ -1136,25 +1077,10 @@ where
let mut termAlts := #[]
for alt in alts do
let rhs toTerm alt.rhs
let termAlt := mkNode ``Parser.Term.matchAlt #[mkAtomFrom alt.ref "|", mkNullNode #[alt.patterns], mkAtomFrom alt.ref "=>", rhs]
let termAlt := mkNode `Lean.Parser.Term.matchAlt #[mkAtomFrom alt.ref "|", mkNullNode #[alt.patterns], mkAtomFrom alt.ref "=>", rhs]
termAlts := termAlts.push termAlt
let termMatchAlts := mkNode ``Parser.Term.matchAlts #[mkNullNode termAlts]
return mkNode ``Parser.Term.«match» #[mkAtomFrom ref "match", genParam, optMotive, discrs, mkAtomFrom ref "with", termMatchAlts]
| .matchExpr ref meta d alts elseBranch => withFreshMacroScope do
let d' `(discr)
let mut termAlts := #[]
for alt in alts do
let rhs toTerm alt.rhs
let optVar := if let some var := alt.var? then mkNullNode #[var, mkAtomFrom var "@"] else mkNullNode #[]
let termAlt := mkNode ``Parser.Term.matchExprAlt #[mkAtomFrom alt.ref "|", optVar, alt.funName, mkNullNode alt.pvars, mkAtomFrom alt.ref "=>", rhs]
termAlts := termAlts.push termAlt
let elseBranch := mkNode ``Parser.Term.matchExprElseAlt #[mkAtomFrom ref "|", mkHole ref, mkAtomFrom ref "=>", ( toTerm elseBranch)]
let termMatchExprAlts := mkNode ``Parser.Term.matchExprAlts #[mkNullNode termAlts, elseBranch]
let body := mkNode ``Parser.Term.matchExpr #[mkAtomFrom ref "match_expr", d', mkAtomFrom ref "with", termMatchExprAlts]
if meta then
`(Bind.bind (instantiateMVarsIfMVarApp $d) fun discr => $body)
else
`(let discr := $d; $body)
let termMatchAlts := mkNode `Lean.Parser.Term.matchAlts #[mkNullNode termAlts]
return mkNode `Lean.Parser.Term.«match» #[mkAtomFrom ref "match", genParam, optMotive, discrs, mkAtomFrom ref "with", termMatchAlts]
def run (code : Code) (m : Syntax) (returnType : Syntax) (uvars : Array Var := #[]) (kind := Kind.regular) : MacroM Syntax :=
toTerm code { m, returnType, kind, uvars }
@@ -1607,23 +1533,6 @@ mutual
let matchCode mkMatch ref genParam discrs optMotive alts
concatWith matchCode doElems
/-- Generate `CodeBlock` for `doMatchExpr; doElems` -/
partial def doMatchExprToCode (doMatchExpr : Syntax) (doElems: List Syntax) : M CodeBlock := do
let ref := doMatchExpr
let meta := doMatchExpr[1].isNone
let discr := doMatchExpr[2]
let alts := doMatchExpr[4][0].getArgs -- Array of `doMatchExprAlt`
let alts alts.mapM fun alt => do
let var? := if alt[1].isNone then none else some alt[1][0]
let funName := alt[2]
let pvars := alt[3].getArgs
let rhs := alt[5]
let rhs doSeqToCode (getDoSeqElems rhs)
pure { ref, var?, funName, pvars, rhs }
let elseBranch doSeqToCode (getDoSeqElems doMatchExpr[4][1][3])
let matchCode mkMatchExpr ref meta discr alts elseBranch
concatWith matchCode doElems
/--
Generate `CodeBlock` for `doTry; doElems`
```
@@ -1731,8 +1640,6 @@ mutual
doForToCode doElem doElems
else if k == ``Parser.Term.doMatch then
doMatchToCode doElem doElems
else if k == ``Parser.Term.doMatchExpr then
doMatchExprToCode doElem doElems
else if k == ``Parser.Term.doTry then
doTryToCode doElem doElems
else if k == ``Parser.Term.doBreak then

View File

@@ -1,136 +0,0 @@
/-
Copyright (c) 2023 Kyle Miller. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Kyle Miller
-/
prelude
import Lean.Server.CodeActions.Attr
/-! `#guard_msgs` command for testing commands
This module defines a command to test that another command produces the expected messages.
See the docstring on the `#guard_msgs` command.
-/
open Lean Parser.Tactic Elab Command
namespace Lean.Elab.Tactic.GuardMsgs
/-- Gives a string representation of a message without source position information.
Ensures the message ends with a '\n'. -/
private def messageToStringWithoutPos (msg : Message) : IO String := do
let mut str msg.data.toString
unless msg.caption == "" do
str := msg.caption ++ ":\n" ++ str
if !("\n".isPrefixOf str) then str := " " ++ str
match msg.severity with
| MessageSeverity.information => str := "info:" ++ str
| MessageSeverity.warning => str := "warning:" ++ str
| MessageSeverity.error => str := "error:" ++ str
if str.isEmpty || str.back != '\n' then
str := str ++ "\n"
return str
/-- The decision made by a specification for a message. -/
inductive SpecResult
/-- Capture the message and check it matches the docstring. -/
| check
/-- Drop the message and delete it. -/
| drop
/-- Do not capture the message. -/
| passthrough
/-- Parses a `guardMsgsSpec`.
- No specification: check everything.
- With a specification: interpret the spec, and if nothing applies pass it through. -/
def parseGuardMsgsSpec (spec? : Option (TSyntax ``guardMsgsSpec)) :
CommandElabM (Message SpecResult) := do
if let some spec := spec? then
match spec with
| `(guardMsgsSpec| ($[$elts:guardMsgsSpecElt],*)) => do
let mut p : Message SpecResult := fun _ => .passthrough
let pushP (s : MessageSeverity) (drop : Bool) (p : Message SpecResult)
(msg : Message) : SpecResult :=
if msg.severity == s then if drop then .drop else .check
else p msg
for elt in elts.reverse do
match elt with
| `(guardMsgsSpecElt| $[drop%$drop?]? info) => p := pushP .information drop?.isSome p
| `(guardMsgsSpecElt| $[drop%$drop?]? warning) => p := pushP .warning drop?.isSome p
| `(guardMsgsSpecElt| $[drop%$drop?]? error) => p := pushP .error drop?.isSome p
| `(guardMsgsSpecElt| $[drop%$drop?]? all) =>
p := fun _ => if drop?.isSome then .drop else .check
| _ => throwErrorAt elt "Invalid #guard_msgs specification element"
return p
| _ => throwErrorAt spec "Invalid #guard_msgs specification"
else
return fun _ => .check
/-- An info tree node corresponding to a failed `#guard_msgs` invocation,
used for code action support. -/
structure GuardMsgFailure where
/-- The result of the nested command -/
res : String
deriving TypeName
@[builtin_command_elab Lean.guardMsgsCmd] def elabGuardMsgs : CommandElab
| `(command| $[$dc?:docComment]? #guard_msgs%$tk $(spec?)? in $cmd) => do
let expected : String := ( dc?.mapM (getDocStringText ·)).getD "" |>.trim
let specFn parseGuardMsgsSpec spec?
let initMsgs modifyGet fun st => (st.messages, { st with messages := {} })
elabCommandTopLevel cmd
let msgs := ( get).messages
let mut toCheck : MessageLog := .empty
let mut toPassthrough : MessageLog := .empty
for msg in msgs.toList do
match specFn msg with
| .check => toCheck := toCheck.add msg
| .drop => pure ()
| .passthrough => toPassthrough := toPassthrough.add msg
let res := "---\n".intercalate ( toCheck.toList.mapM (messageToStringWithoutPos ·)) |>.trim
-- We do some whitespace normalization here to allow users to break long lines.
if expected.replace "\n" " " == res.replace "\n" " " then
-- Passed. Only put toPassthrough messages back on the message log
modify fun st => { st with messages := initMsgs ++ toPassthrough }
else
-- Failed. Put all the messages back on the message log and add an error
modify fun st => { st with messages := initMsgs ++ msgs }
logErrorAt tk m!"❌ Docstring on `#guard_msgs` does not match generated message:\n\n{res}"
pushInfoLeaf (.ofCustomInfo { stx := getRef, value := Dynamic.mk (GuardMsgFailure.mk res) })
| _ => throwUnsupportedSyntax
open CodeAction Server RequestM in
/-- A code action which will update the doc comment on a `#guard_msgs` invocation. -/
@[builtin_command_code_action guardMsgsCmd]
def guardMsgsCodeAction : CommandCodeAction := fun _ _ _ node => do
let .node _ ts := node | return #[]
let res := ts.findSome? fun
| .node (.ofCustomInfo { stx, value }) _ => return (stx, ( value.get? GuardMsgFailure).res)
| _ => none
let some (stx, res) := res | return #[]
let doc readDoc
let eager := {
title := "Update #guard_msgs with tactic output"
kind? := "quickfix"
isPreferred? := true
}
pure #[{
eager
lazy? := some do
let some start := stx.getPos? true | return eager
let some tail := stx.setArg 0 mkNullNode |>.getPos? true | return eager
let newText := if res.isEmpty then
""
else if res.length 100-7 && !res.contains '\n' then -- TODO: configurable line length?
s!"/-- {res} -/\n"
else
s!"/--\n{res}\n-/\n"
pure { eager with
edit? := some <|.ofTextEdit doc.versionedIdentifier {
range := doc.meta.text.utf8RangeToLspRange start, tail
newText
}
}
}]
end Lean.Elab.Tactic.GuardMsgs

View File

@@ -49,25 +49,14 @@ def PartialContextInfo.mergeIntoOuter?
some { outer with parentDecl? := innerParentDecl }
def CompletionInfo.stx : CompletionInfo Syntax
| dot i .. => i.stx
| id stx .. => stx
| dotId stx .. => stx
| fieldId stx .. => stx
| namespaceId stx => stx
| option stx => stx
| dot i .. => i.stx
| id stx .. => stx
| dotId stx .. => stx
| fieldId stx .. => stx
| namespaceId stx => stx
| option stx => stx
| endSection stx .. => stx
| tactic stx .. => stx
/--
Obtains the `LocalContext` from this `CompletionInfo` if available and yields an empty context
otherwise.
-/
def CompletionInfo.lctx : CompletionInfo LocalContext
| dot i .. => i.lctx
| id _ _ _ lctx .. => lctx
| dotId _ _ lctx .. => lctx
| fieldId _ _ lctx .. => lctx
| _ => .empty
| tactic stx .. => stx
def CustomInfo.format : CustomInfo Format
| i => f!"CustomInfo({i.value.typeName})"

View File

@@ -5,7 +5,6 @@ Authors: Leonardo de Moura, Mario Carneiro
-/
prelude
import Lean.Util.ForEachExprWhere
import Lean.Meta.CtorRecognizer
import Lean.Meta.Match.Match
import Lean.Meta.GeneralizeVars
import Lean.Meta.ForEachExpr
@@ -443,7 +442,7 @@ private def applyRefMap (e : Expr) (map : ExprMap Expr) : Expr :=
-/
private def whnfPreservingPatternRef (e : Expr) : MetaM Expr := do
let eNew whnf e
if ( isConstructorApp eNew) then
if eNew.isConstructorApp ( getEnv) then
return eNew
else
return applyRefMap eNew (mkPatternRefMap e)
@@ -474,7 +473,7 @@ partial def normalize (e : Expr) : M Expr := do
let p normalize p
addVar h
return mkApp4 e.getAppFn (e.getArg! 0) x p h
else if ( isMatchValue e) then
else if isMatchValue e then
return e
else if e.isFVar then
if ( isExplicitPatternVar e) then
@@ -572,8 +571,8 @@ private partial def toPattern (e : Expr) : MetaM Pattern := do
match e.getArg! 1, e.getArg! 3 with
| Expr.fvar x, Expr.fvar h => return Pattern.as x p h
| _, _ => throwError "unexpected occurrence of auxiliary declaration 'namedPattern'"
else if ( isMatchValue e) then
return Pattern.val ( normLitValue e)
else if isMatchValue e then
return Pattern.val e
else if e.isFVar then
return Pattern.var e.fvarId!
else

View File

@@ -1,204 +0,0 @@
/-
Copyright (c) 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura
-/
prelude
import Lean.Elab.Term
namespace Lean.Elab.Term
namespace MatchExpr
/--
`match_expr` alternative. Recall that it has the following structure.
```
| (ident "@")? ident bindeIdent* => rhs
```
Example:
```
| c@Eq _ a b => f c a b
```
-/
structure Alt where
/--
`some c` if there is a variable binding to the function symbol being matched.
`c` is the variable name.
-/
var? : Option Ident
/-- Function being matched. -/
funName : Ident
/-- Pattern variables. The list uses `none` for representing `_`, and `some a` for pattern variable `a`. -/
pvars : List (Option Ident)
/-- right-hand-side for the alternative. -/
rhs : Syntax
/-- Store the auxliary continuation function for each right-hand-side. -/
k : Ident := .missing
/-- Actual value to be passed as an argument. -/
actuals : List Term := []
/--
`match_expr` else-alternative. Recall that it has the following structure.
```
| _ => rhs
```
-/
structure ElseAlt where
rhs : Syntax
open Parser Term
/--
Converts syntax representing a `match_expr` else-alternative into an `ElseAlt`.
-/
def toElseAlt? (stx : Syntax) : Option ElseAlt :=
if !stx.isOfKind ``matchExprElseAlt then none else
some { rhs := stx.getArg 3 }
/--
Converts syntax representing a `match_expr` alternative into an `Alt`.
-/
def toAlt? (stx : Syntax) : Option Alt :=
if !stx.isOfKind ``matchExprAlt then none else
let var? : Option Ident :=
let optVar := stx.getArg 1
if optVar.isNone then none else some optVar.getArg 0
let funName := stx.getArg 2
let pvars := stx.getArg 3 |>.getArgs.toList.reverse.map fun arg =>
match arg with
| `(_) => none
| _ => some arg
let rhs := stx.getArg 5
some { var?, funName, pvars, rhs }
/--
Returns the function names of alternatives that do not have any pattern variable left.
-/
def getFunNamesToMatch (alts : List Alt) : List Ident := Id.run do
let mut funNames := #[]
for alt in alts do
if alt.pvars.isEmpty then
if Option.isNone <| funNames.find? fun funName => funName.getId == alt.funName.getId then
funNames := funNames.push alt.funName
return funNames.toList
/--
Returns `true` if there is at least one alternative whose next pattern variable is not a `_`.
-/
def shouldSaveActual (alts : List Alt) : Bool :=
alts.any fun alt => alt.pvars matches some _ :: _
/--
Returns the first alternative whose function name is `funName` **and**
does not have pattern variables left to match.
-/
def getAltFor? (alts : List Alt) (funName : Ident) : Option Alt :=
alts.find? fun alt => alt.funName.getId == funName.getId && alt.pvars.isEmpty
/--
Removes alternatives that do not have any pattern variable left to be matched.
For the ones that still have pattern variables, remove the first one, and
save `actual` if the removed pattern variable is not a `_`.
-/
def next (alts : List Alt) (actual : Term) : List Alt :=
alts.filterMap fun alt =>
if let some _ :: pvars := alt.pvars then
some { alt with pvars, actuals := actual :: alt.actuals }
else if let none :: pvars := alt.pvars then
some { alt with pvars }
else
none
/--
Creates a fresh identifier for representing the continuation function used to
execute the RHS of the given alternative, and stores it in the field `k`.
-/
def initK (alt : Alt) : MacroM Alt := withFreshMacroScope do
let k : Ident `(k)
return { alt with k }
/--
Generates parameters for the continuation function used to execute
the RHS of the given alternative.
-/
def getParams (alt : Alt) : MacroM (Array Term) := do
let mut params := #[]
if let some var := alt.var? then
params := params.push ( `(($var : Expr)))
params := params ++ ( alt.pvars.toArray.reverse.filterMapM fun
| none => return none
| some arg => return some ( `(($arg : Expr))))
if params.isEmpty then
return #[( `(_))]
return params
/--
Generates the actual arguments for invoking the auxiliary continuation function
associated with the given alternative. The arguments are the actuals stored in `alt`.
`discr` is also an argument if `alt.var?` is not none.
-/
def getActuals (discr : Term) (alt : Alt) : MacroM (Array Term) := do
let mut actuals := #[]
if alt.var?.isSome then
actuals := actuals.push discr
actuals := actuals ++ alt.actuals.toArray
if actuals.isEmpty then
return #[ `(())]
return actuals
def toDoubleQuotedName (ident : Ident) : Term :=
mkNode ``Parser.Term.doubleQuotedName #[mkAtom "`", mkAtom "`", ident]
/--
Generates an `if-then-else` tree for implementing a `match_expr` with discriminant `discr`,
alternatives `alts`, and else-alternative `elseAlt`.
-/
partial def generate (discr : Term) (alts : List Alt) (elseAlt : ElseAlt) : MacroM Syntax := do
let alts alts.mapM initK
let discr' `(discr)
let kElse `(ke)
let rec loop (discr : Term) (alts : List Alt) : MacroM Term := withFreshMacroScope do
let funNamesToMatch := getFunNamesToMatch alts
let saveActual := shouldSaveActual alts
let actual if saveActual then `(a) else `(_)
let altsNext := next alts actual
let body if altsNext.isEmpty then
`($kElse ())
else
let discr' `(discr)
let body loop discr' altsNext
if saveActual then
`(if h : ($discr).isApp then let a := Expr.appArg $discr h; let discr := Expr.appFnCleanup $discr h; $body else $kElse ())
else
`(if h : ($discr).isApp then let discr := Expr.appFnCleanup $discr h; $body else $kElse ())
let mut result := body
for funName in funNamesToMatch do
if let some alt := getAltFor? alts funName then
let actuals getActuals discr alt
result `(if ($discr).isConstOf $(toDoubleQuotedName funName) then $alt.k $actuals* else $result)
return result
let body loop discr' alts
let mut result `(let_delayed ke := fun (_ : Unit) => $(elseAlt.rhs):term; let discr := Expr.cleanupAnnotations $discr:term; $body:term)
for alt in alts do
let params getParams alt
result `(let_delayed $alt.k:ident := fun $params:term* => $(alt.rhs):term; $result:term)
return result
def main (discr : Term) (alts : Array Syntax) (elseAlt : Syntax) : MacroM Syntax := do
let alts alts.toList.mapM fun alt =>
if let some alt := toAlt? alt then
pure alt
else
Macro.throwErrorAt alt "unexpected `match_expr` alternative"
let some elseAlt := toElseAlt? elseAlt
| Macro.throwErrorAt elseAlt "unexpected `match_expr` else-alternative"
generate discr alts elseAlt
end MatchExpr
@[builtin_macro Lean.Parser.Term.matchExpr] def expandMatchExpr : Macro := fun stx =>
match stx with
| `(match_expr $discr:term with $alts) =>
MatchExpr.main discr (alts.raw.getArg 0).getArgs (alts.raw.getArg 1)
| _ => Macro.throwUnsupported
end Lean.Elab.Term

View File

@@ -214,7 +214,7 @@ private def expandWhereStructInst : Macro
`(structInstField|$id:ident := $val)
| stx@`(letIdDecl|_ $_* $[: $_]? := $_) => Macro.throwErrorAt stx "'_' is not allowed here"
| _ => Macro.throwUnsupported
let body `(structInst| { $structInstFields,* })
let body `({ $structInstFields,* })
match whereDecls? with
| some whereDecls => expandWhereDecls whereDecls body
| none => return body

View File

@@ -107,10 +107,22 @@ def mkUnexpander (attrKind : TSyntax ``attrKind) (pat qrhs : Term) : OptionT Mac
-- The reference is attached to the syntactic representation of the called function itself, not the entire function application
let lhs `($$f:ident)
let lhs := Syntax.mkApp lhs (.mk args)
-- allow over-application, avoiding nested `app` nodes
let lhsWithMoreArgs := flattenApp ( `($lhs $$moreArgs*))
let patWithMoreArgs := flattenApp ( `($pat $$moreArgs*))
`(@[$attrKind app_unexpander $(mkIdent c)]
aux_def unexpand $(mkIdent c) : Lean.PrettyPrinter.Unexpander := fun
| `($lhs) => withRef f `($pat)
-- must be a separate case as the LHS and RHS above might not be `app` nodes
| `($lhsWithMoreArgs) => withRef f `($patWithMoreArgs)
| _ => throw ())
where
-- NOTE: we consider only one nesting level here
flattenApp : Term Term
| stx@`($f $xs*) => match f with
| `($f' $xs'*) => Syntax.mkApp f' (xs' ++ xs)
| _ => stx
| stx => stx
private def expandNotationAux (ref : Syntax) (currNamespace : Name)
(doc? : Option (TSyntax ``docComment))

View File

@@ -159,19 +159,6 @@ partial def collect (stx : Syntax) : M Syntax := withRef stx <| withFreshMacroSc
discard <| processVar h
``(_root_.namedPattern $id $pat $h)
else if k == ``Lean.Parser.Term.binop then
/-
We support `binop%` syntax in patterns because we
wanted to support `x+1` in patterns.
Recall that the `binop%` syntax was added to improve elaboration of some binary operators: `+` is one of them.
Recall that `HAdd.hAdd` is marked as `[match_pattern]`
TODO for a distant future: make this whole procedure extensible.
-/
-- Check whether the `binop%` operator is marked with `[match_pattern]`,
-- We must check that otherwise Lean will accept operators that are not tagged with this annotation.
let some (.const fName _) resolveId? stx[1] "pattern"
| throwCtorExpected
unless hasMatchPatternAttribute ( getEnv) fName do
throwCtorExpected
let lhs collect stx[2]
let rhs collect stx[3]
return stx.setArg 2 lhs |>.setArg 3 rhs

View File

@@ -5,7 +5,6 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Meta.Eqns
import Lean.Meta.CtorRecognizer
import Lean.Util.CollectFVars
import Lean.Util.ForEachExprWhere
import Lean.Meta.Tactic.Split
@@ -219,14 +218,13 @@ where
-/
private def shouldUseSimpMatch (e : Expr) : MetaM Bool := do
let env getEnv
let find (root : Expr) : ExceptT Unit MetaM Unit :=
root.forEach fun e => do
if let some info := isMatcherAppCore? env e then
let args := e.getAppArgs
for discr in args[info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs] do
if ( Meta.isConstructorApp discr) then
throwThe Unit ()
return ( (find e).run) matches .error _
return Option.isSome <| e.find? fun e => Id.run do
if let some info := isMatcherAppCore? env e then
let args := e.getAppArgs
for discr in args[info.getFirstDiscrPos : info.getFirstDiscrPos + info.numDiscrs] do
if discr.isConstructorApp env then
return true
return false
partial def mkEqnTypes (declNames : Array Name) (mvarId : MVarId) : MetaM (Array Expr) := do
let (_, eqnTypes) go mvarId |>.run { declNames } |>.run #[]

View File

@@ -121,7 +121,8 @@ def addPreDefinitions (preDefs : Array PreDefinition) : TermElabM Unit := withLC
preDefs.forM (·.termination.ensureNone "partial")
else
try
let hasHints := preDefs.any fun preDef => preDef.termination.isNotNone
let hasHints := preDefs.any fun preDef =>
preDef.termination.decreasing_by?.isSome || preDef.termination.termination_by?.isSome
if hasHints then
wfRecursion preDefs
else

View File

@@ -5,7 +5,7 @@ Authors: Leonardo de Moura
-/
prelude
import Lean.Util.HasConstCache
import Lean.Meta.Match.MatcherApp.Transform
import Lean.Meta.Match.Match
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic

View File

@@ -37,12 +37,12 @@ where
return ()
else if ( tryContradiction mvarId) then
return ()
else if let some mvarId whnfReducibleLHS? mvarId then
go mvarId
else if let some mvarId simpMatch? mvarId then
go mvarId
else if let some mvarId simpIf? mvarId then
go mvarId
else if let some mvarId whnfReducibleLHS? mvarId then
go mvarId
else match ( simpTargetStar mvarId {} (simprocs := {})).1 with
| TacticResultCNM.closed => return ()
| TacticResultCNM.modified mvarId => go mvarId

View File

@@ -6,7 +6,6 @@ Authors: Leonardo de Moura
prelude
import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Structural.Basic
import Lean.Meta.Match.MatcherApp.Basic
namespace Lean.Elab.Structural
open Meta

View File

@@ -5,10 +5,9 @@ Authors: Joachim Breitner
-/
prelude
import Lean.Util.HasConstCache
import Lean.Meta.Match.MatcherApp.Transform
import Lean.Meta.Match.Match
import Lean.Meta.Tactic.Cleanup
import Lean.Meta.Tactic.Refl
import Lean.Meta.Tactic.TryThis
import Lean.Elab.Quotation
import Lean.Elab.RecAppSyntax
import Lean.Elab.PreDefinition.Basic
@@ -703,19 +702,17 @@ def guessLex (preDefs : Array PreDefinition) (unaryPreDef : PreDefinition)
-- Collect all recursive calls and extract their context
let recCalls collectRecCalls unaryPreDef fixedPrefixSize arities
let recCalls := filterSubsumed recCalls
let rcs recCalls.mapM (RecCallCache.mk (preDefs.map (·.termination.decreasingBy?)) ·)
let rcs recCalls.mapM (RecCallCache.mk (preDefs.map (·.termination.decreasing_by?)) ·)
let callMatrix := rcs.map (inspectCall ·)
match liftMetaM <| solve measures callMatrix with
| .some solution => do
let wf buildTermWF originalVarNamess varNamess solution
let wf' := trimTermWF extraParamss wf
for preDef in preDefs, term in wf' do
if showInferredTerminationBy.get ( getOptions) then
logInfoAt preDef.ref m!"Inferred termination argument:\n{← term.unexpand}"
if let some ref := preDef.termination.terminationBy?? then
Tactic.TryThis.addSuggestion ref ( term.unexpand)
if showInferredTerminationBy.get ( getOptions) then
let wf' := trimTermWF extraParamss wf
for preDef in preDefs, term in wf' do
logInfoAt preDef.ref m!"Inferred termination argument: {← term.unexpand}"
return wf
| .none =>

View File

@@ -94,12 +94,12 @@ def wfRecursion (preDefs : Array PreDefinition) : TermElabM Unit := do
return ( packMutual fixedPrefixSize preDefs unaryPreDefs, fixedPrefixSize)
let wf do
let (preDefsWith, preDefsWithout) := preDefs.partition (·.termination.terminationBy?.isSome)
let (preDefsWith, preDefsWithout) := preDefs.partition (·.termination.termination_by?.isSome)
if preDefsWith.isEmpty then
-- No termination_by anywhere, so guess one
guessLex preDefs unaryPreDef fixedPrefixSize
else if preDefsWithout.isEmpty then
pure <| preDefsWith.map (·.termination.terminationBy?.get!)
pure <| preDefsWith.map (·.termination.termination_by?.get!)
else
-- Some have, some do not, so report errors
preDefsWithout.forM fun preDef => do
@@ -114,7 +114,7 @@ def wfRecursion (preDefs : Array PreDefinition) : TermElabM Unit := do
trace[Elab.definition.wf] "wfRel: {wfRel}"
let (value, envNew) withoutModifyingEnv' do
addAsAxiom unaryPreDef
let value mkFix unaryPreDef prefixArgs wfRel (preDefs.map (·.termination.decreasingBy?))
let value mkFix unaryPreDef prefixArgs wfRel (preDefs.map (·.termination.decreasing_by?))
eraseRecAppSyntaxExpr value
/- `mkFix` invokes `decreasing_tactic` which may add auxiliary theorems to the environment. -/
let value unfoldDeclsFrom envNew value

View File

@@ -68,14 +68,7 @@ def elabWFRel (preDefs : Array PreDefinition) (unaryPreDefName : Name) (fixedPre
for (d, mvarId) in subgoals, element in wf, preDef in preDefs do
let mvarId unpackUnary preDef fixedPrefixSize mvarId d element
mvarId.withContext do
let errorMsgHeader? := if preDefs.size > 1 then
"The termination argument types differ for the different functions, or depend on the " ++
"function's varying parameters. Try using `sizeOf` explicitly:\nThe termination argument"
else
"The termination argument depends on the function's varying parameters. Try using " ++
"`sizeOf` explicitly:\nThe termination argument"
let value Term.withSynthesize <| elabTermEnsuringType element.body ( mvarId.getType)
(errorMsgHeader? := errorMsgHeader?)
mvarId.assign value
let wfRelVal synthInstance ( inferType (mkMVar wfRelMVarId))
wfRelMVarId.assign wfRelVal

View File

@@ -27,7 +27,7 @@ structure TerminationBy where
deriving Inhabited
open Parser.Termination in
def TerminationBy.unexpand (wf : TerminationBy) : MetaM (TSyntax ``terminationBy) := do
def TerminationBy.unexpand (wf : TerminationBy) : MetaM Syntax := do
-- TODO: Why can I not just use $wf.vars in the quotation below?
let vars : TSyntaxArray `ident := wf.vars.map (·.raw)
if vars.isEmpty then
@@ -50,9 +50,8 @@ is what `Term.runTactic` expects.
-/
structure TerminationHints where
ref : Syntax
terminationBy?? : Option Syntax
terminationBy? : Option TerminationBy
decreasingBy? : Option DecreasingBy
termination_by? : Option TerminationBy
decreasing_by? : Option DecreasingBy
/-- Here we record the number of parameters past the `:`. It is set by
`TerminationHints.rememberExtraParams` and used as folows:
@@ -64,27 +63,19 @@ structure TerminationHints where
extraParams : Nat
deriving Inhabited
def TerminationHints.none : TerminationHints := .missing, .none, .none, .none, 0
def TerminationHints.none : TerminationHints := .missing, .none, .none, 0
/-- Logs warnings when the `TerminationHints` are present. -/
def TerminationHints.ensureNone (hints : TerminationHints) (reason : String): CoreM Unit := do
match hints.terminationBy??, hints.terminationBy?, hints.decreasingBy? with
| .none, .none, .none => pure ()
| .none, .none, .some dec_by =>
match hints.termination_by?, hints.decreasing_by? with
| .none, .none => pure ()
| .none, .some dec_by =>
logErrorAt dec_by.ref m!"unused `decreasing_by`, function is {reason}"
| .some term_by?, .none, .none =>
logErrorAt term_by? m!"unused `termination_by?`, function is {reason}"
| .none, .some term_by, .none =>
| .some term_by, .none =>
logErrorAt term_by.ref m!"unused `termination_by`, function is {reason}"
| _, _, _ =>
| .some _, .some _ =>
logErrorAt hints.ref m!"unused termination hints, function is {reason}"
/-- True if any form of termination hint is present. -/
def TerminationHints.isNotNone (hints : TerminationHints) : Bool :=
hints.terminationBy??.isSome ||
hints.terminationBy?.isSome ||
hints.decreasingBy?.isSome
/--
Remembers `extraParams` for later use. Needs to happen early enough where we still know
how many parameters came from the function header (`headerParams`).
@@ -120,23 +111,19 @@ def elabTerminationHints {m} [Monad m] [MonadError m] (stx : TSyntax ``suffix) :
if let .missing := stx.raw then
return { TerminationHints.none with ref := stx }
match stx with
| `(suffix| $[$t?]? $[$d?:decreasingBy]? ) => do
let terminationBy?? : Option Syntax if let some t := t? then match t with
| `(terminationBy?|termination_by?) => pure (some t)
| _ => pure none
else pure none
let terminationBy? : Option TerminationBy if let some t := t? then match t with
| `(terminationBy|termination_by => $_body) =>
throwErrorAt t "no extra parameters bounds, please omit the `=>`"
| `(terminationBy|termination_by $vars* => $body) => pure (some {ref := t, vars, body})
| `(terminationBy|termination_by $body:term) => pure (some {ref := t, vars := #[], body})
| `(terminationBy?|termination_by?) => pure none
| `(suffix| $[$t?:terminationBy]? $[$d?:decreasingBy]? ) => do
let termination_by? t?.mapM fun t => match t with
| `(terminationBy|termination_by $vars* => $body) =>
if vars.isEmpty then
throwErrorAt t "no extra parameters bounds, please omit the `=>`"
else
pure {ref := t, vars, body}
| `(terminationBy|termination_by $body:term) => pure {ref := t, vars := #[], body}
| _ => throwErrorAt t "unexpected `termination_by` syntax"
else pure none
let decreasingBy? d?.mapM fun d => match d with
let decreasing_by? d?.mapM fun d => match d with
| `(decreasingBy|decreasing_by $tactic) => pure {ref := d, tactic}
| _ => throwErrorAt d "unexpected `decreasing_by` syntax"
return { ref := stx, terminationBy??, terminationBy?, decreasingBy?, extraParams := 0 }
return { ref := stx, termination_by?, decreasing_by?, extraParams := 0 }
| _ => throwErrorAt stx s!"Unexpected Termination.suffix syntax: {stx} of kind {stx.raw.getKind}"
end Lean.Elab.WF

View File

@@ -77,27 +77,9 @@ where
go sources (sourcesNew.push source)
else
withFreshMacroScope do
/-
Recall that local variables starting with `__` are treated as impl detail.
See `LocalContext.lean`.
Moreover, implementation detail let-vars are unfolded by `simp`
even when `zetaDelta := false`.
Motivation: the following failure when `zetaDelta := true`
```
structure A where
a : Nat
structure B extends A where
b : Nat
w : a = b
def x : A where a := 37
@[simp] theorem x_a : x.a = 37 := rfl
def y : B := { x with b := 37, w := by simp }
```
-/
let sourceNew `(__src)
let sourceNew `(src)
let r go sources (sourcesNew.push sourceNew)
`(let __src := $source; $r)
`(let src := $source; $r)
structure ExplicitSourceInfo where
stx : Syntax

View File

@@ -13,7 +13,6 @@ import Lean.Elab.Tactic.Injection
import Lean.Elab.Tactic.Match
import Lean.Elab.Tactic.Rewrite
import Lean.Elab.Tactic.Location
import Lean.Elab.Tactic.SimpTrace
import Lean.Elab.Tactic.Simp
import Lean.Elab.Tactic.Simproc
import Lean.Elab.Tactic.BuiltinTactic
@@ -33,8 +32,3 @@ import Lean.Elab.Tactic.Change
import Lean.Elab.Tactic.FalseOrByContra
import Lean.Elab.Tactic.Omega
import Lean.Elab.Tactic.Simpa
import Lean.Elab.Tactic.NormCast
import Lean.Elab.Tactic.Symm
import Lean.Elab.Tactic.SolveByElim
import Lean.Elab.Tactic.LibrarySearch
import Lean.Elab.Tactic.ShowTerm

View File

@@ -372,24 +372,10 @@ private def preprocessPropToDecide (expectedType : Expr) : TermElabM Expr := do
let expectedType preprocessPropToDecide expectedType
let d mkDecide expectedType
let d instantiateMVars d
-- Get instance from `d`
let s := d.appArg!
-- Reduce the instance rather than `d` itself, since that gives a nicer error message on failure.
let r withDefault <| whnf s
if r.isAppOf ``isFalse then
throwError "\
tactic 'decide' proved that the proposition\
{indentExpr expectedType}\n\
is false"
unless r.isAppOf ``isTrue do
throwError "\
tactic 'decide' failed for proposition\
{indentExpr expectedType}\n\
since its 'Decidable' instance reduced to\
{indentExpr r}\n\
rather than to the 'isTrue' constructor."
-- While we have a proof from reduction, we do not embed it in the proof term,
-- but rather we let the kernel recompute it during type checking from a more efficient term.
let r withDefault <| whnf d
unless r.isConstOf ``true do
throwError "failed to reduce to 'true'{indentExpr r}"
let s := d.appArg! -- get instance from `d`
let rflPrf mkEqRefl (toExpr true)
return mkApp3 (Lean.mkConst ``of_decide_eq_true) expectedType s rflPrf

View File

@@ -1,81 +0,0 @@
/-
Copyright (c) 2021-2024 Gabriel Ebner and Lean FRO. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner, Joe Hendrix, Scott Morrison
-/
prelude
import Lean.Meta.Tactic.LibrarySearch
import Lean.Meta.Tactic.TryThis
import Lean.Elab.Tactic.ElabTerm
namespace Lean.Elab.LibrarySearch
open Lean Meta LibrarySearch
open Elab Tactic Term TryThis
/--
Implementation of the `exact?` tactic.
* `ref` contains the input syntax and is used for locations in error reporting.
* `required` contains an optional list of terms that should be used in closing the goal.
* `requireClose` indicates if the goal must be closed.
It is `true` for `exact?` and `false` for `apply?`.
-/
def exact? (ref : Syntax) (required : Option (Array (TSyntax `term))) (requireClose : Bool) :
TacticM Unit := do
let mvar getMainGoal
let (_, goal) ( getMainGoal).intros
goal.withContext do
let required := ( (required.getD #[]).mapM getFVarId).toList.map .fvar
let tactic := fun exfalso =>
solveByElim required (exfalso := exfalso) (maxDepth := 6)
let allowFailure := fun g => do
let g g.withContext (instantiateMVars (.mvar g))
return required.all fun e => e.occurs g
match librarySearch goal tactic allowFailure with
-- Found goal that closed problem
| none =>
addExactSuggestion ref ( instantiateMVars (mkMVar mvar)).headBeta
-- Found suggestions
| some suggestions =>
if requireClose then throwError
"`exact?` could not close the goal. Try `apply?` to see partial suggestions."
reportOutOfHeartbeats `library_search ref
for (_, suggestionMCtx) in suggestions do
withMCtx suggestionMCtx do
addExactSuggestion ref ( instantiateMVars (mkMVar mvar)).headBeta (addSubgoalsMsg := true)
if suggestions.isEmpty then logError "apply? didn't find any relevant lemmas"
admitGoal goal
@[builtin_tactic Lean.Parser.Tactic.exact?]
def evalExact : Tactic := fun stx => do
let `(tactic| exact? $[using $[$required],*]?) := stx
| throwUnsupportedSyntax
exact? ( getRef) required true
@[builtin_tactic Lean.Parser.Tactic.apply?]
def evalApply : Tactic := fun stx => do
let `(tactic| apply? $[using $[$required],*]?) := stx
| throwUnsupportedSyntax
exact? ( getRef) required false
@[builtin_term_elab Lean.Parser.Syntax.exact?]
def elabExact?Term : TermElab := fun stx expectedType? => do
let `(exact?%) := stx | throwUnsupportedSyntax
withExpectedType expectedType? fun expectedType => do
let goal mkFreshExprMVar expectedType
let (_, introdGoal) goal.mvarId!.intros
introdGoal.withContext do
if let some suggestions librarySearch introdGoal then
reportOutOfHeartbeats `library_search stx
for suggestion in suggestions do
withMCtx suggestion.2 do
addTermSuggestion stx ( instantiateMVars goal).headBeta
if suggestions.isEmpty then logError "exact?# didn't find any relevant lemmas"
mkSorry expectedType (synthetic := true)
else
addTermSuggestion stx ( instantiateMVars goal).headBeta
instantiateMVars goal
end Lean.Elab.LibrarySearch

View File

@@ -1,274 +0,0 @@
/-
Copyright (c) 2019 Paul-Nicolas Madelaine. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Paul-Nicolas Madelaine, Robert Y. Lewis, Mario Carneiro, Gabriel Ebner
-/
prelude
import Lean.Meta.Tactic.NormCast
import Lean.Elab.Tactic.Conv.Simp
import Lean.Elab.ElabRules
/-!
# The `norm_cast` family of tactics.
A full description of the tactic, and the use of each theorem category, can be found at
<https://arxiv.org/abs/2001.10594>.
-/
namespace Lean.Elab.Tactic.NormCast
open Lean Meta Simp NormCast
-- TODO: trace name consistency
builtin_initialize registerTraceClass `Tactic.norm_cast
/-- Proves `a = b` using the given simp set. -/
def proveEqUsing (s : SimpTheorems) (a b : Expr) : MetaM (Option Simp.Result) := do
let go : SimpM (Option Simp.Result) := do
let a' Simp.simp a
let b' Simp.simp b
unless isDefEq a'.expr b'.expr do return none
a'.mkEqTrans ( b'.mkEqSymm b)
withReducible do
(go ( Simp.mkDefaultMethods).toMethodsRef
{ simpTheorems := #[s], congrTheorems := Meta.getSimpCongrTheorems }).run' {}
/-- Proves `a = b` by simplifying using move and squash lemmas. -/
def proveEqUsingDown (a b : Expr) : MetaM (Option Simp.Result) := do
withTraceNode `Tactic.norm_cast (return m!"{exceptOptionEmoji ·} proving: {← mkEq a b}") do
proveEqUsing ( normCastExt.down.getTheorems) a b
/-- Constructs the expression `(e : ty)`. -/
def mkCoe (e : Expr) (ty : Expr) : MetaM Expr := do
let .some e' coerce? e ty | failure
return e'
/--
Checks whether an expression is the coercion of some other expression,
and if so returns that expression.
-/
def isCoeOf? (e : Expr) : MetaM (Option Expr) := do
if let Expr.const fn .. := e.getAppFn then
if let some info getCoeFnInfo? fn then
if e.getAppNumArgs == info.numArgs then
return e.getArg! info.coercee
return none
/--
Checks whether an expression is a numeral in some type,
and if so returns that type and the natural number.
-/
def isNumeral? (e : Expr) : Option (Expr × Nat) :=
-- TODO: cleanup, and possibly remove duplicate
if e.isConstOf ``Nat.zero then
(mkConst ``Nat, 0)
else if let Expr.app (Expr.app (Expr.app (Expr.const ``OfNat.ofNat ..) α ..)
(Expr.lit (Literal.natVal n) ..) ..) .. := e then
some (α, n)
else
none
/--
This is the main heuristic used alongside the elim and move lemmas.
The goal is to help casts move past operators by adding intermediate casts.
An expression of the shape:
```
op (↑(x : α) : γ) (↑(y : β) : γ)
```
is rewritten to:
```
op (↑(↑(x : α) : β) : γ) (↑(y : β) : γ)
```
when
```
(↑(↑(x : α) : β) : γ) = (↑(x : α) : γ)
```
can be proven with a squash lemma
-/
def splittingProcedure (expr : Expr) : MetaM Simp.Result := do
let Expr.app (Expr.app op x ..) y .. := expr | return {expr}
let Expr.forallE _ γ (Expr.forallE _ γ' ty ..) .. inferType op | return {expr}
if γ'.hasLooseBVars || ty.hasLooseBVars then return {expr}
unless isDefEq γ γ' do return {expr}
let msg := m!"splitting {expr}"
let msg
| .error _ => return m!"{bombEmoji} {msg}"
| .ok r => return if r.expr == expr then m!"{crossEmoji} {msg}" else
m!"{checkEmoji} {msg} to {r.expr}"
withTraceNode `Tactic.norm_cast msg do
try
let some x' isCoeOf? x | failure
let some y' isCoeOf? y | failure
let α inferType x'
let β inferType y'
-- TODO: fast timeout
(try
let x2 mkCoe ( mkCoe x' β) γ
let some x_x2 proveEqUsingDown x x2 | failure
Simp.mkCongrFun ( Simp.mkCongr {expr := op} x_x2) y
catch _ =>
let y2 mkCoe ( mkCoe y' α) γ
let some y_y2 proveEqUsingDown y y2 | failure
Simp.mkCongr {expr := mkApp op x} y_y2)
catch _ => try
let some (_, n) := isNumeral? y | failure
let some x' isCoeOf? x | failure
let α inferType x'
let y2 mkCoe ( mkNumeral α n) γ
let some y_y2 proveEqUsingDown y y2 | failure
Simp.mkCongr {expr := mkApp op x} y_y2
catch _ => try
let some (_, n) := isNumeral? x | failure
let some y' isCoeOf? y | failure
let β inferType y'
let x2 mkCoe ( mkNumeral β n) γ
let some x_x2 proveEqUsingDown x x2 | failure
Simp.mkCongrFun ( Simp.mkCongr {expr := op} x_x2) y
catch _ =>
return {expr}
/--
Discharging function used during simplification in the "squash" step.
-/
-- TODO: normCast takes a list of expressions to use as lemmas for the discharger
-- TODO: a tactic to print the results the discharger fails to prove
def prove (e : Expr) : SimpM (Option Expr) := do
withTraceNode `Tactic.norm_cast (return m!"{exceptOptionEmoji ·} discharging: {e}") do
return ( findLocalDeclWithType? e).map mkFVar
/--
Core rewriting function used in the "squash" step, which moves casts upwards
and eliminates them.
It tries to rewrite an expression using the elim and move lemmas.
On failure, it calls the splitting procedure heuristic.
-/
partial def upwardAndElim (up : SimpTheorems) (e : Expr) : SimpM Simp.Step := do
let r withDischarger prove do
Simp.rewrite? e up.post up.erased (tag := "squash") (rflOnly := false)
let r := r.getD { expr := e }
let r r.mkEqTrans ( splittingProcedure r.expr)
if r.expr == e then return Simp.Step.done {expr := e}
return Simp.Step.visit r
/--
If possible, rewrites `(n : α)` to `(Nat.cast n : α)` where `n` is a numeral and `α`.
Returns a pair of the new expression and proof that they are equal.
-/
def numeralToCoe (e : Expr) : MetaM Simp.Result := do
let some (α, n) := isNumeral? e | failure
if ( whnf α).isConstOf ``Nat then failure
let newE mkAppOptM ``Nat.cast #[α, none, toExpr n]
let some pr proveEqUsingDown e newE | failure
return pr
/--
The core simplification routine of `normCast`.
-/
def derive (e : Expr) : MetaM Simp.Result := do
withTraceNode `Tactic.norm_cast (fun _ => return m!"{e}") do
let e instantiateMVars e
let config : Simp.Config := {
zeta := false
beta := false
eta := false
proj := false
iota := false
}
let congrTheorems Meta.getSimpCongrTheorems
let r : Simp.Result := { expr := e }
let withTrace phase := withTraceNode `Tactic.norm_cast fun
| .ok r => return m!"{r.expr} (after {phase})"
| .error _ => return m!"{bombEmoji} {phase}"
-- step 1: pre-processing of numerals
let r withTrace "pre-processing numerals" do
let post e := return Simp.Step.done ( try numeralToCoe e catch _ => pure {expr := e})
r.mkEqTrans ( Simp.main r.expr { config, congrTheorems } (methods := { post })).1
-- step 2: casts are moved upwards and eliminated
let r withTrace "moving upward, splitting and eliminating" do
let post := upwardAndElim ( normCastExt.up.getTheorems)
r.mkEqTrans ( Simp.main r.expr { config, congrTheorems } (methods := { post })).1
-- step 3: casts are squashed
let r withTrace "squashing" do
let simpTheorems := #[ normCastExt.squash.getTheorems]
r.mkEqTrans ( simp r.expr { simpTheorems, config, congrTheorems }).1
return r
open Term
@[builtin_term_elab modCast] def elabModCast : TermElab := fun stx expectedType? => do
match stx with
| `(mod_cast $e:term) =>
withExpectedType expectedType? fun expectedType => do
if ( instantiateMVars expectedType).hasExprMVar then tryPostpone
let expectedType' derive expectedType
let e Term.elabTerm e expectedType'.expr
synthesizeSyntheticMVars
let eTy instantiateMVars ( inferType e)
if eTy.hasExprMVar then tryPostpone
let eTy' derive eTy
unless isDefEq eTy'.expr expectedType'.expr do
throwTypeMismatchError "mod_cast" expectedType'.expr eTy'.expr e
let eTy_eq_expectedType eTy'.mkEqTrans ( expectedType'.mkEqSymm expectedType )
eTy_eq_expectedType.mkCast e
| _ => throwUnsupportedSyntax
/-- Implementation of the `norm_cast` tactic when operating on the main goal. -/
def normCastTarget : TacticM Unit :=
liftMetaTactic1 fun goal => do
let tgt instantiateMVars ( goal.getType)
let prf derive tgt
applySimpResultToTarget goal tgt prf
/-- Implementation of the `norm_cast` tactic when operating on a hypothesis. -/
def normCastHyp (fvarId : FVarId) : TacticM Unit :=
liftMetaTactic1 fun goal => do
let hyp instantiateMVars ( fvarId.getDecl).type
let prf derive hyp
return ( applySimpResultToLocalDecl goal fvarId prf false).map (·.snd)
@[builtin_tactic normCast0]
def evalNormCast0 : Tactic := fun stx => do
match stx with
| `(tactic| norm_cast0 $[$loc?]?) =>
let loc := if let some loc := loc? then expandLocation loc else Location.targets #[] true
withMainContext do
match loc with
| Location.targets hyps target =>
if target then normCastTarget
( getFVarIds hyps).forM normCastHyp
| Location.wildcard =>
normCastTarget
( ( getMainGoal).getNondepPropHyps).forM normCastHyp
| _ => throwUnsupportedSyntax
@[builtin_tactic Lean.Parser.Tactic.Conv.normCast]
def evalConvNormCast : Tactic :=
open Elab.Tactic.Conv in fun _ => withMainContext do
applySimpResult ( derive ( getLhs))
@[builtin_tactic pushCast]
def evalPushCast : Tactic := fun stx => do
let { ctx, simprocs, dischargeWrapper } withMainContext do
mkSimpContext (simpTheorems := pushCastExt.getTheorems) stx (eraseLocal := false)
let ctx := { ctx with config := { ctx.config with failIfUnchanged := false } }
dischargeWrapper.with fun discharge? =>
discard <| simpLocation ctx simprocs discharge? (expandOptLocation stx[5])
open Command in
@[builtin_command_elab Parser.Tactic.normCastAddElim] def elabAddElim : CommandElab := fun stx => do
match stx with
| `(norm_cast_add_elim $id:ident) =>
Elab.Command.liftCoreM do MetaM.run' do
addElim ( resolveGlobalConstNoOverload id)
| _ => throwUnsupportedSyntax
end Lean.Elab.Tactic.NormCast

View File

@@ -3,7 +3,6 @@ Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Lean.Elab.Tactic.Omega.Frontend
/-!

View File

@@ -3,8 +3,6 @@ Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Omega.Constraint
import Lean.Elab.Tactic.Omega.OmegaM
import Lean.Elab.Tactic.Omega.MinNatAbs
@@ -505,37 +503,29 @@ Decides which variable to run Fourier-Motzkin elimination on, and returns the as
We prefer eliminations which introduce no new inequalities, or otherwise exact eliminations,
and break ties by the number of new inequalities introduced.
-/
def fourierMotzkinSelect (data : Array FourierMotzkinData) : MetaM FourierMotzkinData := do
def fourierMotzkinSelect (data : Array FourierMotzkinData) : FourierMotzkinData := Id.run do
let data := data.filter fun d => ¬ d.isEmpty
trace[omega] "Selecting variable to eliminate from (idx, size, exact) triples:\n\
{data.map fun d => (d.var, d.size, d.exact)}"
let mut bestIdx := 0
let mut bestSize := data[0]!.size
let mut bestExact := data[0]!.exact
if bestSize = 0 then
trace[omega] "Selected variable {data[0]!.var}."
return data[0]!
if bestSize = 0 then return data[0]!
for i in [1:data.size] do
let exact := data[i]!.exact
let size := data[i]!.size
if size = 0 || !bestExact && exact || (bestExact == exact) && size < bestSize then
if size = 0 then
trace[omega] "Selected variable {data[i]!.var}"
return data[i]!
if size = 0 || !bestExact && exact || size < bestSize then
if size = 0 then return data[i]!
bestIdx := i
bestExact := exact
bestSize := size
trace[omega] "Selected variable {data[bestIdx]!.var}."
return data[bestIdx]!
/--
Run Fourier-Motzkin elimination on one variable.
-/
-- This is only in MetaM to enable tracing.
def fourierMotzkin (p : Problem) : MetaM Problem := do
def fourierMotzkin (p : Problem) : Problem := Id.run do
let data := p.fourierMotzkinData
-- Now perform the elimination.
let _, irrelevant, lower, upper, _, _ fourierMotzkinSelect data
let _, irrelevant, lower, upper, _, _ := fourierMotzkinSelect data
let mut r : Problem := { assumptions := p.assumptions, eliminations := p.eliminations }
for f in irrelevant do
r := r.insertConstraint f
@@ -564,7 +554,7 @@ partial def elimination (p : Problem) : OmegaM Problem :=
return p
else do
trace[omega] "Running Fourier-Motzkin elimination on:\n{p}"
runOmega ( p.fourierMotzkin)
runOmega p.fourierMotzkin
else
return p

View File

@@ -3,7 +3,6 @@ Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Lean.Elab.Tactic.Omega.Core
import Lean.Elab.Tactic.FalseOrByContra
import Lean.Meta.Tactic.Cases
@@ -24,13 +23,6 @@ Allow elaboration of `OmegaConfig` arguments to tactics.
-/
declare_config_elab elabOmegaConfig Lean.Meta.Omega.OmegaConfig
/-- Match on the two defeq expressions for successor: `n+1`, `n.succ`. -/
def succ? (e : Expr) : Option Expr :=
match e.getAppFnArgs with
| (``Nat.succ, #[n]) => some n
| (``HAdd.hAdd, #[_, _, _, _, a, b]) => do
if b == toExpr (1 : Nat) then some a else none
| _ => none
/--
A partially processed `omega` context.
@@ -68,7 +60,7 @@ def mkEvalRflProof (e : Expr) (lc : LinearCombo) : OmegaM Expr := do
`e = (coordinate n).eval atoms`. -/
def mkCoordinateEvalAtomsEq (e : Expr) (n : Nat) : OmegaM Expr := do
if n < 10 then
let atoms atoms
let atoms := ( getThe State).atoms
let tail mkListLit (.const ``Int []) atoms[n+1:].toArray.toList
let lem := .str ``LinearCombo s!"coordinate_eval_{n}"
mkEqSymm (mkAppN (.const lem []) (atoms[:n+1].toArray.push tail))
@@ -84,6 +76,13 @@ def mkAtomLinearCombo (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × HashSet
let (n, facts) lookup e
return LinearCombo.coordinate n, mkCoordinateEvalAtomsEq e n, facts.getD
-- This has been PR'd as
-- https://github.com/leanprover/lean4/pull/2900
-- and can be removed when that is merged.
@[inherit_doc mkAppN]
local macro_rules
| `(mkAppN $f #[$xs,*]) => (xs.getElems.foldlM (fun x e => `(Expr.app $x $e)) f : MacroM Term)
mutual
/--
@@ -122,7 +121,7 @@ We also transform the expression as we descend into it:
-/
partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr × HashSet Expr) := do
trace[omega] "processing {e}"
match groundInt? e with
match e.int? with
| some i =>
let lc := {const := i}
return lc, mkEvalRflProof e lc,
@@ -185,20 +184,17 @@ partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr ×
| some r => pure r
| none => mkAtomLinearCombo e
| (``HMod.hMod, #[_, _, _, _, n, k]) =>
match groundNat? k with
| some k' => do
let k' := toExpr (k' : Int)
rewrite ( mkAppM ``HMod.hMod #[n, k']) (mkApp2 (.const ``Int.emod_def []) n k')
match natCast? k with
| some _ => rewrite e (mkApp2 (.const ``Int.emod_def []) n k)
| none => mkAtomLinearCombo e
| (``HDiv.hDiv, #[_, _, _, _, x, z]) =>
match groundInt? z with
match intCast? z with
| some 0 => rewrite e (mkApp (.const ``Int.ediv_zero []) x)
| some i => do
let e' mkAppM ``HDiv.hDiv #[x, toExpr i]
| some i =>
if i < 0 then
rewrite e' (mkApp2 (.const ``Int.ediv_neg []) x (toExpr (-i)))
rewrite e (mkApp2 (.const ``Int.ediv_neg []) x (toExpr (-i)))
else
mkAtomLinearCombo e'
mkAtomLinearCombo e
| _ => mkAtomLinearCombo e
| (``Min.min, #[_, _, a, b]) =>
if ( cfg).splitMinMax then
@@ -210,15 +206,39 @@ partial def asLinearComboImpl (e : Expr) : OmegaM (LinearCombo × OmegaM Expr ×
rewrite e (mkApp2 (.const ``Int.max_def []) a b)
else
mkAtomLinearCombo e
| (``HPow.hPow, #[_, _, _, _, b, k]) =>
match succ? k with /- match for `e+1` and `e.succ` -/
| none => mkAtomLinearCombo e
| some k' =>
match groundInt? b with
| some _ => rewrite e (mkApp2 (.const ``Int.pow_succ []) b k')
| none => mkAtomLinearCombo e
| (``Nat.cast, #[.const ``Int [], i, n]) =>
handleNatCast e i n
match n with
| .fvar h =>
if let some v h.getValue? then
rewrite e ( mkEqReflWithExpectedType e
(mkApp3 (.const ``Nat.cast [0]) (.const ``Int []) i v))
else
mkAtomLinearCombo e
| _ => match n.getAppFnArgs with
| (``Nat.succ, #[n]) => rewrite e (.app (.const ``Int.ofNat_succ []) n)
| (``HAdd.hAdd, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_add []) a b)
| (``HMul.hMul, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_mul []) a b)
| (``HDiv.hDiv, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_ediv []) a b)
| (``OfNat.ofNat, #[_, n, _]) => rewrite e (.app (.const ``Int.natCast_ofNat []) n)
| (``HMod.hMod, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_emod []) a b)
| (``HSub.hSub, #[_, _, _, _, mkAppN (.const ``HSub.hSub _) #[_, _, _, _, a, b], c]) =>
rewrite e (mkApp3 (.const ``Int.ofNat_sub_sub []) a b c)
| (``Prod.fst, #[_, β, p]) => match p with
| .app (.app (.app (.app (.const ``Prod.mk [0, v]) _) _) x) y =>
rewrite e (mkApp3 (.const ``Int.ofNat_fst_mk [v]) β x y)
| _ => mkAtomLinearCombo e
| (``Prod.snd, #[α, _, p]) => match p with
| .app (.app (.app (.app (.const ``Prod.mk [u, 0]) _) _) x) y =>
rewrite e (mkApp3 (.const ``Int.ofNat_snd_mk [u]) α x y)
| _ => mkAtomLinearCombo e
| (``Min.min, #[_, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_min []) a b)
| (``Max.max, #[_, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_max []) a b)
| (``Int.natAbs, #[n]) =>
if ( cfg).splitNatAbs then
rewrite e (mkApp (.const ``Int.ofNat_natAbs []) n)
else
mkAtomLinearCombo e
| _ => mkAtomLinearCombo e
| (``Prod.fst, #[α, β, p]) => match p with
| .app (.app (.app (.app (.const ``Prod.mk [u, v]) _) _) x) y =>
rewrite e (mkApp4 (.const ``Prod.fst_mk [u, v]) α x β y)
@@ -241,78 +261,6 @@ where
let prf' : OmegaM Expr := do mkEqTrans rw ( prf)
pure (lc, prf', facts)
| none => panic! "Invalid rewrite rule in 'asLinearCombo'"
handleNatCast (e i n : Expr) : OmegaM (LinearCombo × OmegaM Expr × HashSet Expr) := do
match n with
| .fvar h =>
if let some v h.getValue? then
rewrite e ( mkEqReflWithExpectedType e
(mkApp3 (.const ``Nat.cast [0]) (.const ``Int []) i v))
else
mkAtomLinearCombo e
| _ => match n.getAppFnArgs with
| (``Nat.succ, #[n]) => rewrite e (.app (.const ``Int.ofNat_succ []) n)
| (``HAdd.hAdd, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_add []) a b)
| (``HMul.hMul, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_mul []) a b)
| (``HDiv.hDiv, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_ediv []) a b)
| (``OfNat.ofNat, #[_, n, _]) => rewrite e (.app (.const ``Int.natCast_ofNat []) n)
| (``HMod.hMod, #[_, _, _, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_emod []) a b)
| (``HSub.hSub, #[_, _, _, _, mkApp6 (.const ``HSub.hSub _) _ _ _ _ a b, c]) =>
rewrite e (mkApp3 (.const ``Int.ofNat_sub_sub []) a b c)
| (``HPow.hPow, #[_, _, _, _, a, b]) =>
match groundNat? a with
| some _ => rewrite e (mkApp2 (.const ``Int.ofNat_pow []) a b)
| none => mkAtomLinearCombo e
| (``Prod.fst, #[_, β, p]) => match p with
| .app (.app (.app (.app (.const ``Prod.mk [0, v]) _) _) x) y =>
rewrite e (mkApp3 (.const ``Int.ofNat_fst_mk [v]) β x y)
| _ => mkAtomLinearCombo e
| (``Prod.snd, #[α, _, p]) => match p with
| .app (.app (.app (.app (.const ``Prod.mk [u, 0]) _) _) x) y =>
rewrite e (mkApp3 (.const ``Int.ofNat_snd_mk [u]) α x y)
| _ => mkAtomLinearCombo e
| (``Min.min, #[_, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_min []) a b)
| (``Max.max, #[_, _, a, b]) => rewrite e (mkApp2 (.const ``Int.ofNat_max []) a b)
| (``HShiftLeft.hShiftLeft, #[_, _, _, _, a, b]) =>
rewrite e (mkApp2 (.const ``Int.ofNat_shiftLeft_eq []) a b)
| (``HShiftRight.hShiftRight, #[_, _, _, _, a, b]) =>
rewrite e (mkApp2 (.const ``Int.ofNat_shiftRight_eq_div_pow []) a b)
| (``Int.natAbs, #[n]) =>
if ( cfg).splitNatAbs then
rewrite e (mkApp (.const ``Int.ofNat_natAbs []) n)
else
mkAtomLinearCombo e
| (``Fin.val, #[n, x]) =>
handleFinVal e i n x
| _ => mkAtomLinearCombo e
handleFinVal (e i n x : Expr) : OmegaM (LinearCombo × OmegaM Expr × HashSet Expr) := do
match x with
| .fvar h =>
if let some v h.getValue? then
rewrite e ( mkEqReflWithExpectedType e
(mkApp3 (.const ``Nat.cast [0]) (.const ``Int []) i (mkApp2 (.const ``Fin.val []) n v)))
else
mkAtomLinearCombo e
| _ => match x.getAppFnArgs, n.nat? with
| (``HAdd.hAdd, #[_, _, _, _, a, b]), _ =>
rewrite e (mkApp3 (.const ``Fin.ofNat_val_add []) n a b)
| (``HMul.hMul, #[_, _, _, _, a, b]), _ =>
rewrite e (mkApp3 (.const ``Fin.ofNat_val_mul []) n a b)
| (``HSub.hSub, #[_, _, _, _, a, b]), some _ =>
-- Only do this rewrite if `n` is a numeral.
rewrite e (mkApp3 (.const ``Fin.ofNat_val_sub []) n a b)
| (``OfNat.ofNat, #[_, y, _]), some m =>
-- Only do this rewrite if `n` is a nonzero numeral.
if m = 0 then
mkAtomLinearCombo e
else
match y with
| .lit (.natVal y) =>
rewrite e (mkApp4 (.const ``Fin.ofNat_val_natCast [])
(toExpr (m - 1)) (toExpr y) (.lit (.natVal (y % m))) ( mkEqRefl (toExpr (y % m))))
| _ =>
-- This shouldn't happen, we obtained `y` from `OfNat.ofNat`
mkAtomLinearCombo e
| _, _ => mkAtomLinearCombo e
end
namespace MetaProblem
@@ -375,17 +323,11 @@ def pushNot (h P : Expr) : MetaM (Option Expr) := do
return some (mkApp3 (.const ``Nat.le_of_not_lt []) x y h)
| (``LE.le, #[.const ``Nat [], _, x, y]) =>
return some (mkApp3 (.const ``Nat.lt_of_not_le []) x y h)
| (``LT.lt, #[.app (.const ``Fin []) n, _, x, y]) =>
return some (mkApp4 (.const ``Fin.le_of_not_lt []) n x y h)
| (``LE.le, #[.app (.const ``Fin []) n, _, x, y]) =>
return some (mkApp4 (.const ``Fin.lt_of_not_le []) n x y h)
| (``Eq, #[.const ``Nat [], x, y]) =>
return some (mkApp3 (.const ``Nat.lt_or_gt_of_ne []) x y h)
| (``Eq, #[.const ``Int [], x, y]) =>
return some (mkApp3 (.const ``Int.lt_or_gt_of_ne []) x y h)
| (``Prod.Lex, _) => return some ( mkAppM ``Prod.of_not_lex #[h])
| (``Eq, #[.app (.const ``Fin []) n, x, y]) =>
return some (mkApp4 (.const ``Fin.lt_or_gt_of_ne []) n x y h)
| (``Dvd.dvd, #[.const ``Nat [], _, k, x]) =>
return some (mkApp3 (.const ``Nat.emod_pos_of_not_dvd []) k x h)
| (``Dvd.dvd, #[.const ``Int [], _, k, x]) =>
@@ -460,16 +402,6 @@ partial def addFact (p : MetaProblem) (h : Expr) : OmegaM (MetaProblem × Nat) :
p.addFact (mkApp3 (.const ``Nat.mod_eq_zero_of_dvd []) k x h)
| (``Dvd.dvd, #[.const ``Int [], _, k, x]) =>
p.addFact (mkApp3 (.const ``Int.emod_eq_zero_of_dvd []) k x h)
| (``Eq, #[.app (.const ``Fin []) n, x, y]) =>
p.addFact (mkApp4 (.const ``Fin.val_congr []) n x y h)
| (``LE.le, #[.app (.const ``Fin []) n, _, x, y]) =>
p.addFact (mkApp4 (.const ``Fin.val_le_of_le []) n x y h)
| (``LT.lt, #[.app (.const ``Fin []) n, _, x, y]) =>
p.addFact (mkApp4 (.const ``Fin.val_add_one_le_of_lt []) n x y h)
| (``GE.ge, #[.app (.const ``Fin []) n, _, x, y]) =>
p.addFact (mkApp4 (.const ``Fin.val_le_of_ge []) n x y h)
| (``GT.gt, #[.app (.const ``Fin []) n, _, x, y]) =>
p.addFact (mkApp4 (.const ``Fin.val_add_one_le_of_gt []) n x y h)
| (``And, #[t₁, t₂]) => do
let (p₁, n₁) p.addFact (mkApp3 (.const ``And.left []) t₁ t₂ h)
let (p₂, n₂) p₁.addFact (mkApp3 (.const ``And.right []) t₁ t₂ h)
@@ -573,6 +505,7 @@ partial def omegaImpl (m : MetaProblem) (g : MVarId) : OmegaM Unit := g.withCont
trace[omega] "Justification:\n{p'.explanation?.get}"
let prf instantiateMVars ( prf)
trace[omega] "omega found a contradiction, proving {← inferType prf}"
trace[omega] "{prf}"
g.assign prf
end
@@ -598,7 +531,7 @@ def omegaTactic (cfg : OmegaConfig) : TacticM Unit := do
/-- The `omega` tactic, for resolving integer and natural linear arithmetic problems. This
`TacticM Unit` frontend with default configuration can be used as an Aesop rule, for example via
the tactic call `aesop (add 50% tactic Lean.Omega.omegaDefault)`. -/
the tactic call `aesop (add 50% tactic Std.Tactic.Omega.omegaDefault)`. -/
def omegaDefault : TacticM Unit := omegaTactic {}
@[builtin_tactic Lean.Parser.Tactic.omega]
@@ -607,7 +540,3 @@ def evalOmega : Tactic := fun
let cfg elabOmegaConfig (mkOptionalNode cfg)
omegaTactic cfg
| _ => throwUnsupportedSyntax
builtin_initialize bvOmegaSimpExtension : SimpExtension
registerSimpAttr `bv_toNat
"simp lemmas converting `BitVec` goals to `Nat` goals, for the `bv_omega` preprocessor"

View File

@@ -3,10 +3,6 @@ Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.BinderPredicates
import Init.Data.List
import Init.Data.Option
/-!
# `List.nonzeroMinimum`, `List.minNatAbs`, `List.maxNatAbs`

View File

@@ -3,11 +3,6 @@ Copyright (c) 2023 Lean FRO, LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison
-/
prelude
import Init.Omega.LinearCombo
import Init.Omega.Int
import Init.Omega.Logic
import Init.Data.BitVec
import Lean.Meta.AppBuilder
/-!
@@ -51,7 +46,7 @@ structure Context where
/-- The internal state for the `OmegaM` monad, recording previously encountered atoms. -/
structure State where
/-- The atoms up-to-defeq encountered so far. -/
atoms : HashMap Expr Nat := {}
atoms : Array Expr := #[]
/-- An intermediate layer in the `OmegaM` monad. -/
abbrev OmegaM' := StateRefT State (ReaderT Context MetaM)
@@ -76,11 +71,10 @@ def OmegaM.run (m : OmegaM α) (cfg : OmegaConfig) : MetaM α :=
def cfg : OmegaM OmegaConfig := do pure ( read).cfg
/-- Retrieve the list of atoms. -/
def atoms : OmegaM (Array Expr) := do
return ( getThe State).atoms.toArray.qsort (·.2 < ·.2) |>.map (·.1)
def atoms : OmegaM (List Expr) := return ( getThe State).atoms.toList
/-- Return the `Expr` representing the list of atoms. -/
def atomsList : OmegaM Expr := do mkListLit (.const ``Int []) ( atoms).toList
def atomsList : OmegaM Expr := do mkListLit (.const ``Int []) ( atoms)
/-- Return the `Expr` representing the list of atoms as a `Coeffs`. -/
def atomsCoeffs : OmegaM Expr := do
@@ -114,45 +108,6 @@ def intCast? (n : Expr) : Option Int :=
| (``Nat.cast, #[_, _, n]) => n.nat?
| _ => n.int?
/--
If `groundNat? e = some n`, then `e` is definitionally equal to `OfNat.ofNat n`.
-/
-- We may want to replace this with an implementation using
-- the internals of `simp (config := {ground := true})`
partial def groundNat? (e : Expr) : Option Nat :=
match e.getAppFnArgs with
| (``Nat.cast, #[_, _, n]) => groundNat? n
| (``HAdd.hAdd, #[_, _, _, _, x, y]) => op (· + ·) x y
| (``HMul.hMul, #[_, _, _, _, x, y]) => op (· * ·) x y
| (``HSub.hSub, #[_, _, _, _, x, y]) => op (· - ·) x y
| (``HDiv.hDiv, #[_, _, _, _, x, y]) => op (· / ·) x y
| (``HPow.hPow, #[_, _, _, _, x, y]) => op (· ^ ·) x y
| _ => e.nat?
where op (f : Nat Nat Nat) (x y : Expr) : Option Nat :=
match groundNat? x, groundNat? y with
| some x', some y' => some (f x' y')
| _, _ => none
/--
If `groundInt? e = some i`,
then `e` is definitionally equal to the standard expression for `i`.
-/
partial def groundInt? (e : Expr) : Option Int :=
match e.getAppFnArgs with
| (``Nat.cast, #[_, _, n]) => groundNat? n
| (``HAdd.hAdd, #[_, _, _, _, x, y]) => op (· + ·) x y
| (``HMul.hMul, #[_, _, _, _, x, y]) => op (· * ·) x y
| (``HSub.hSub, #[_, _, _, _, x, y]) => op (· - ·) x y
| (``HDiv.hDiv, #[_, _, _, _, x, y]) => op (· / ·) x y
| (``HPow.hPow, #[_, _, _, _, x, y]) => match groundInt? x, groundNat? y with
| some x', some y' => some (x' ^ y')
| _, _ => none
| _ => e.int?
where op (f : Int Int Int) (x y : Expr) : Option Int :=
match groundNat? x, groundNat? y with
| some x', some y' => some (f x' y')
| _, _ => none
/-- Construct the term with type hint `(Eq.refl a : a = b)`-/
def mkEqReflWithExpectedType (a b : Expr) : MetaM Expr := do
mkExpectedTypeHint ( mkEqRefl a) ( mkEq a b)
@@ -175,8 +130,6 @@ def analyzeAtom (e : Expr) : OmegaM (HashSet Expr) := do
r := r.insert (mkApp (.const ``Int.neg_le_natAbs []) x)
| _, (``Fin.val, #[n, i]) =>
r := r.insert (mkApp2 (.const ``Fin.isLt []) n i)
| _, (``BitVec.toNat, #[n, x]) =>
r := r.insert (mkApp2 (.const ``BitVec.toNat_lt []) n x)
| _, _ => pure ()
return r
| (``HDiv.hDiv, #[_, _, _, _, x, k]) => match natCast? k with
@@ -244,16 +197,15 @@ Return its index, and, if it is new, a collection of interesting facts about the
-/
def lookup (e : Expr) : OmegaM (Nat × Option (HashSet Expr)) := do
let c getThe State
match c.atoms.find? e with
| some i => return (i, none)
| none =>
for h : i in [:c.atoms.size] do
if isDefEq e c.atoms[i] then
return (i, none)
trace[omega] "New atom: {e}"
let facts analyzeAtom e
if isTracingEnabledFor `omega then
unless facts.isEmpty do
trace[omega] "New facts: {← facts.toList.mapM fun e => inferType e}"
let i modifyGetThe State fun c =>
(c.atoms.size, { c with atoms := c.atoms.insert e c.atoms.size })
let i modifyGetThe State fun c => (c.atoms.size, { c with atoms := c.atoms.push e })
return (i, some facts)
end Omega

View File

@@ -1,28 +0,0 @@
/-
Copyright (c) 2021 Scott Morrison. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Scott Morrison, Mario Carneiro
-/
prelude
import Lean.Elab.ElabRules
import Lean.Meta.Tactic.TryThis
namespace Std.Tactic
open Lean Elab Term Tactic Meta.Tactic.TryThis Parser.Tactic
@[builtin_tactic showTerm] def evalShowTerm : Tactic := fun stx =>
match stx with
| `(tactic| show_term%$tk $t) => withMainContext do
let g getMainGoal
evalTactic t
addExactSuggestion tk ( instantiateMVars (mkMVar g)).headBeta (origSpan? := getRef)
| _ => throwUnsupportedSyntax
/-- Implementation of `show_term` term elaborator. -/
@[builtin_term_elab showTermElabImpl] def elabShowTerm : TermElab
| `(show_term_elab%$tk $t), ty => do
let e Term.elabTermEnsuringType t ty
Term.synthesizeSyntheticMVarsNoPostponing
addTermSuggestion tk ( instantiateMVars e).headBeta (origSpan? := getRef)
pure e
| _, _ => throwUnsupportedSyntax

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