Compare commits

..

1 Commits

Author SHA1 Message Date
Leonardo de Moura
45ce6dda4b feat: warn grind redundant parameters
This PR produces a warning for redundant `grind` arguments.
2025-08-02 07:22:33 +02:00
1018 changed files with 3244 additions and 4333 deletions

View File

@@ -1,9 +1,9 @@
#!/usr/bin/env bash
set -euxo pipefail
set -euo pipefail
cmake --preset release -DUSE_LAKE=ON 1>&2
# We benchmark against stage2/bin to test new optimizations.
# We benchmark against stage 2 to test new optimizations.
timeout -s KILL 1h time make -C build/release -j$(nproc) stage3 1>&2
export PATH=$PWD/build/release/stage2/bin:$PATH
@@ -11,86 +11,6 @@ export PATH=$PWD/build/release/stage2/bin:$PATH
# easier to configure them statically.
cmake -B build/release/stage3 -S src -DLEAN_EXTRA_LAKEFILE_TOML='weakLeanArgs=["-Dprofiler=true", "-Dprofiler.threshold=9999999", "--stats"]' 1>&2
(
cd tests/bench
timeout -s KILL 1h time temci exec --config speedcenter.yaml --in speedcenter.exec.velcom.yaml 1>&2
temci report run_output.yaml --reporter codespeed2
)
if [ -d .git ]; then
DIR="$(git rev-parse @)"
BASE_URL="https://speed.lean-lang.org/lean4-out/$DIR"
{
cat <<'EOF'
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lakeprof Report</title>
</head>
<h1>Lakeprof Report</h1>
<button type="button" id="btn_fetch">View build trace in Perfetto</button>
<script type="text/javascript">
const ORIGIN = 'https://ui.perfetto.dev';
const btnFetch = document.getElementById('btn_fetch');
async function fetchAndOpen(traceUrl) {
const resp = await fetch(traceUrl);
// Error checking is left as an exercise to the reader.
const blob = await resp.blob();
const arrayBuffer = await blob.arrayBuffer();
openTrace(arrayBuffer, traceUrl);
}
function openTrace(arrayBuffer, traceUrl) {
const win = window.open(ORIGIN);
if (!win) {
btnFetch.style.background = '#f3ca63';
btnFetch.onclick = () => openTrace(arrayBuffer);
btnFetch.innerText = 'Popups blocked, click here to open the trace file';
return;
}
const timer = setInterval(() => win.postMessage('PING', ORIGIN), 50);
const onMessageHandler = (evt) => {
if (evt.data !== 'PONG') return;
// We got a PONG, the UI is ready.
window.clearInterval(timer);
window.removeEventListener('message', onMessageHandler);
const reopenUrl = new URL(location.href);
reopenUrl.hash = `#reopen=${traceUrl}`;
win.postMessage({
perfetto: {
buffer: arrayBuffer,
title: 'Lake Build Trace',
url: reopenUrl.toString(),
}}, ORIGIN);
};
window.addEventListener('message', onMessageHandler);
}
// This is triggered when following the link from the Perfetto UI's sidebar.
if (location.hash.startsWith('#reopen=')) {
const traceUrl = location.hash.substr(8);
fetchAndOpen(traceUrl);
}
EOF
cat <<EOF
btnFetch.onclick = () => fetchAndOpen("$BASE_URL/lakeprof.trace_event");
</script>
EOF
echo "<pre><code>"
(cd src; lakeprof report -prc)
echo "</code></pre>"
echo "</body></html>"
} | tee index.html
curl -T index.html $BASE_URL/index.html
curl -T src/lakeprof.log $BASE_URL/lakeprof.log
curl -T src/lakeprof.trace_event $BASE_URL/lakeprof.trace_event
fi

View File

@@ -28,28 +28,6 @@ repositories:
branch: main
dependencies: []
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: plausible
url: https://github.com/leanprover-community/plausible
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: import-graph
url: https://github.com/leanprover-community/import-graph
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- lean4-cli
- name: doc-gen4
url: https://github.com/leanprover/doc-gen4
toolchain-tag: true
@@ -57,6 +35,13 @@ repositories:
branch: main
dependencies: [lean4-cli]
- name: verso
url: https://github.com/leanprover/verso
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: reference-manual
url: https://github.com/leanprover/reference-manual
toolchain-tag: true
@@ -80,6 +65,22 @@ repositories:
dependencies:
- batteries
- name: import-graph
url: https://github.com/leanprover-community/import-graph
toolchain-tag: true
stable-branch: false
branch: main
dependencies:
- lean4-cli
- batteries
- name: plausible
url: https://github.com/leanprover-community/plausible
toolchain-tag: true
stable-branch: false
branch: main
dependencies: []
- name: mathlib4
url: https://github.com/leanprover-community/mathlib4
toolchain-tag: true

View File

@@ -684,17 +684,12 @@ if (LLVM AND ${STAGE} GREATER 0)
set(EXTRA_LEANMAKE_OPTS "LLVM=1")
endif()
set(STDLIBS Init Std Lean Leanc)
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
list(APPEND STDLIBS Lake)
endif()
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
# for a parallelized nested build, but CMake doesn't let us do that.
# We use `lean` from the previous stage, but `leanc`, headers, etc. from the current stage
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make ${STDLIBS}
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Init Std Lean Leanc
VERBATIM)
# if we have LLVM enabled, then build `lean.h.bc` which has the LLVM bitcode
@@ -738,9 +733,14 @@ else()
endif()
if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
add_custom_target(lake_shared
add_custom_target(lake_lib
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS leanshared
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make Lake
VERBATIM)
add_custom_target(lake_shared
WORKING_DIRECTORY ${LEAN_SOURCE_DIR}
DEPENDS lake_lib
COMMAND $(MAKE) -f ${CMAKE_BINARY_DIR}/stdlib.make libLake_shared
VERBATIM)
add_custom_target(lake ALL

View File

@@ -1165,7 +1165,7 @@ Examples:
def zipIdx (xs : Array α) (start := 0) : Array (α × Nat) :=
xs.mapIdx fun i a => (a, start + i)
@[deprecated zipIdx (since := "2025-01-21")] abbrev zipWithIndex := @zipIdx
/--
Returns the first element of the array for which the predicate `p` returns `true`, or `none` if no
@@ -1285,7 +1285,7 @@ def findFinIdx? {α : Type u} (p : α → Bool) (as : Array α) : Option (Fin as
decreasing_by simp_wf; decreasing_trivial_pre_omega
loop 0
private theorem findIdx?_loop_eq_map_findFinIdx?_loop_val {xs : Array α} {p : α Bool} {j} :
theorem findIdx?_loop_eq_map_findFinIdx?_loop_val {xs : Array α} {p : α Bool} {j} :
findIdx?.loop p xs j = (findFinIdx?.loop p xs j).map (·.val) := by
unfold findIdx?.loop
unfold findFinIdx?.loop
@@ -1322,7 +1322,8 @@ def idxOfAux [BEq α] (xs : Array α) (v : α) (i : Nat) : Option (Fin xs.size)
else none
decreasing_by simp_wf; decreasing_trivial_pre_omega
@[deprecated idxOfAux (since := "2025-01-29")]
abbrev indexOfAux := @idxOfAux
/--
Returns the index of the first element equal to `a`, or the size of the array if no element is equal
@@ -1337,7 +1338,8 @@ Examples:
def finIdxOf? [BEq α] (xs : Array α) (v : α) : Option (Fin xs.size) :=
idxOfAux xs v 0
@[deprecated "`Array.indexOf?` has been deprecated, use `idxOf?` or `finIdxOf?` instead." (since := "2025-01-29")]
abbrev indexOf? := @finIdxOf?
/--
Returns the index of the first element equal to `a`, or the size of the array if no element is equal

View File

@@ -123,9 +123,15 @@ abbrev pop_toList := @Array.toList_pop
@[simp, grind =] theorem append_empty {xs : Array α} : xs ++ #[] = xs := by
apply ext'; simp only [toList_append, List.append_nil]
@[deprecated append_empty (since := "2025-01-13")]
abbrev append_nil := @append_empty
@[simp, grind =] theorem empty_append {xs : Array α} : #[] ++ xs = xs := by
apply ext'; simp only [toList_append, List.nil_append]
@[deprecated empty_append (since := "2025-01-13")]
abbrev nil_append := @empty_append
@[simp, grind _=_] theorem append_assoc {xs ys zs : Array α} : xs ++ ys ++ zs = xs ++ (ys ++ zs) := by
apply ext'; simp only [toList_append, List.append_assoc]
@@ -136,6 +142,7 @@ abbrev pop_toList := @Array.toList_pop
rw [ appendList_eq_append]; unfold Array.appendList
induction l generalizing xs <;> simp [*]
@[deprecated toList_appendList (since := "2024-12-11")]
abbrev appendList_toList := @toList_appendList
end Array

View File

@@ -8,20 +8,19 @@ module
prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.List.Range
public import all Init.Data.List.Control
public import Init.Data.List.Nat.TakeDrop
public import Init.Data.List.Nat.Modify
public import Init.Data.List.Nat.Basic
public import Init.Data.List.Monadic
public import Init.Data.List.OfFn
public import all Init.Data.Array.Bootstrap
public import Init.Data.Array.Mem
public import Init.Data.Array.DecidableEq
public import Init.Data.Array.Lex.Basic
public import Init.Data.Range.Lemmas
public import Init.TacticsExtra
public import Init.Data.List.ToArray
import all Init.Data.List.Control
import all Init.Data.Array.Basic
import all Init.Data.Array.Bootstrap
public section
@@ -838,10 +837,16 @@ theorem mem_of_contains_eq_true [BEq α] [LawfulBEq α] {a : α} {as : Array α}
cases as
simp
@[deprecated mem_of_contains_eq_true (since := "2024-12-12")]
abbrev mem_of_elem_eq_true := @mem_of_contains_eq_true
theorem contains_eq_true_of_mem [BEq α] [LawfulBEq α] {a : α} {as : Array α} (h : a as) : as.contains a = true := by
cases as
simpa using h
@[deprecated contains_eq_true_of_mem (since := "2024-12-12")]
abbrev elem_eq_true_of_mem := @contains_eq_true_of_mem
@[simp] theorem elem_eq_contains [BEq α] {a : α} {xs : Array α} :
elem a xs = xs.contains a := by
simp [elem]
@@ -899,9 +904,15 @@ theorem all_push {xs : Array α} {a : α} {p : α → Bool} :
cases xs
simp
@[deprecated getElem_set_self (since := "2024-12-11")]
abbrev getElem_set_eq := @getElem_set_self
@[simp] theorem getElem?_set_self {xs : Array α} {i : Nat} (h : i < xs.size) {v : α} :
(xs.set i v)[i]? = some v := by simp [h]
@[deprecated getElem?_set_self (since := "2024-12-11")]
abbrev getElem?_set_eq := @getElem?_set_self
@[simp] theorem getElem_set_ne {xs : Array α} {i : Nat} (h' : i < xs.size) {v : α} {j : Nat}
(pj : j < xs.size) (h : i j) :
(xs.set i v)[j]'(by simp [*]) = xs[j] := by
@@ -992,6 +1003,9 @@ grind_pattern mem_or_eq_of_mem_set => a ∈ xs.set i b
theorem setIfInBounds_def (xs : Array α) (i : Nat) (a : α) :
xs.setIfInBounds i a = if h : i < xs.size then xs.set i a else xs := rfl
@[deprecated set!_eq_setIfInBounds (since := "2024-12-12")]
abbrev set!_is_setIfInBounds := @set!_eq_setIfInBounds
@[simp, grind] theorem size_setIfInBounds {xs : Array α} {i : Nat} {a : α} :
(xs.setIfInBounds i a).size = xs.size := by
if h : i < xs.size then
@@ -1013,6 +1027,9 @@ theorem setIfInBounds_def (xs : Array α) (i : Nat) (a : α) :
simp at h
simp only [setIfInBounds, h, reduceDIte, getElem_set_self]
@[deprecated getElem_setIfInBounds_self (since := "2024-12-11")]
abbrev getElem_setIfInBounds_eq := @getElem_setIfInBounds_self
@[simp] theorem getElem_setIfInBounds_ne {xs : Array α} {i : Nat} {a : α} {j : Nat}
(hj : j < xs.size) (h : i j) :
(xs.setIfInBounds i a)[j]'(by simpa using hj) = xs[j] := by
@@ -1032,6 +1049,9 @@ theorem getElem?_setIfInBounds_self_of_lt {xs : Array α} {i : Nat} {a : α} (h
(xs.setIfInBounds i a)[i]? = some a := by
simp [h]
@[deprecated getElem?_setIfInBounds_self (since := "2024-12-11")]
abbrev getElem?_setIfInBounds_eq := @getElem?_setIfInBounds_self
@[simp] theorem getElem?_setIfInBounds_ne {xs : Array α} {i j : Nat} (h : i j) {a : α} :
(xs.setIfInBounds i a)[j]? = xs[j]? := by
simp [getElem?_setIfInBounds, h]
@@ -1357,6 +1377,23 @@ theorem mapM_eq_mapM_toList [Monad m] [LawfulMonad m] {f : α → m β} {xs : Ar
toList <$> xs.mapM f = xs.toList.mapM f := by
simp [mapM_eq_mapM_toList]
@[deprecated "Use `mapM_eq_foldlM` instead" (since := "2025-01-08")]
theorem mapM_map_eq_foldl {as : Array α} {f : α β} {i : Nat} :
mapM.map (m := Id) (pure <| f ·) as i b = pure (as.foldl (start := i) (fun acc a => acc.push (f a)) b) := by
unfold mapM.map
split <;> rename_i h
· ext : 1
dsimp [foldl, foldlM]
rw [mapM_map_eq_foldl, dif_pos (by omega), foldlM.loop, dif_pos h]
-- Calling `split` here gives a bad goal.
have : size as - i = Nat.succ (size as - i - 1) := by omega
rw [this]
simp [foldl, foldlM, Nat.sub_add_eq]
· dsimp [foldl, foldlM]
rw [dif_pos (by omega), foldlM.loop, dif_neg h]
rfl
termination_by as.size - i
/--
Use this as `induction ass using array₂_induction` on a hypothesis of the form `ass : Array (Array α)`.
The hypothesis `ass` will be replaced with a hypothesis `ass : List (List α)`,
@@ -2962,6 +2999,9 @@ theorem getElem?_extract {xs : Array α} {start stop : Nat} :
· rw [size_extract, Nat.min_self, Nat.sub_zero]
· intros; rw [getElem_extract]; congr; rw [Nat.zero_add]
@[deprecated extract_size (since := "2025-01-19")]
abbrev extract_all := @extract_size
theorem extract_empty_of_stop_le_start {xs : Array α} {start stop : Nat} (h : stop start) :
xs.extract start stop = #[] := by
simp only [extract, Nat.sub_eq, emptyWithCapacity_eq]
@@ -2996,14 +3036,14 @@ theorem take_size {xs : Array α} : xs.take xs.size = xs := by
/-! ### shrink -/
@[simp] private theorem size_shrink_loop {xs : Array α} {n : Nat} : (shrink.loop n xs).size = xs.size - n := by
@[simp] theorem size_shrink_loop {xs : Array α} {n : Nat} : (shrink.loop n xs).size = xs.size - n := by
induction n generalizing xs with
| zero => simp [shrink.loop]
| succ n ih =>
simp [shrink.loop, ih]
omega
@[simp] private theorem getElem_shrink_loop {xs : Array α} {n i : Nat} (h : i < (shrink.loop n xs).size) :
@[simp] theorem getElem_shrink_loop {xs : Array α} {n i : Nat} (h : i < (shrink.loop n xs).size) :
(shrink.loop n xs)[i] = xs[i]'(by simp at h; omega) := by
induction n generalizing xs i with
| zero => simp [shrink.loop]
@@ -4279,7 +4319,7 @@ Examples:
/-! ### Preliminaries about `ofFn` -/
@[simp] private theorem size_ofFn_go {n} {f : Fin n α} {i acc h} :
@[simp] theorem size_ofFn_go {n} {f : Fin n α} {i acc h} :
(ofFn.go f acc i h).size = acc.size + i := by
induction i generalizing acc with
| zero => simp [ofFn.go]
@@ -4289,7 +4329,7 @@ Examples:
@[simp] theorem size_ofFn {n : Nat} {f : Fin n α} : (ofFn f).size = n := by simp [ofFn]
-- Recall `ofFn.go f acc i h = acc ++ #[f (n - i), ..., f(n - 1)]`
private theorem getElem_ofFn_go {f : Fin n α} {acc i k} (h : i n) (w₁ : k < acc.size + i) :
theorem getElem_ofFn_go {f : Fin n α} {acc i k} (h : i n) (w₁ : k < acc.size + i) :
(ofFn.go f acc i h)[k]'(by simpa using w₁) =
if w₂ : k < acc.size then acc[k] else f n - i + k - acc.size, by omega := by
induction i generalizing acc k with
@@ -4694,10 +4734,27 @@ end List
/-! ### Deprecations -/
namespace Array
@[deprecated size_toArray (since := "2024-12-11")]
theorem size_mk (as : List α) : (Array.mk as).size = as.length := by simp
@[deprecated getElem?_eq_getElem (since := "2024-12-11")]
theorem getElem?_lt
(xs : Array α) {i : Nat} (h : i < xs.size) : xs[i]? = some xs[i] := dif_pos h
@[deprecated getElem?_eq_none (since := "2024-12-11")]
theorem getElem?_ge
(xs : Array α) {i : Nat} (h : i xs.size) : xs[i]? = none := dif_neg (Nat.not_lt_of_le h)
set_option linter.deprecated false in
@[deprecated "`get?` is deprecated" (since := "2025-02-12"), simp]
theorem get?_eq_getElem? (xs : Array α) (i : Nat) : xs.get? i = xs[i]? := rfl
@[deprecated getElem?_eq_none (since := "2024-12-11")]
theorem getElem?_len_le (xs : Array α) {i : Nat} (h : xs.size i) : xs[i]? = none := by
simp [h]
@[deprecated getD_getElem? (since := "2024-12-11")] abbrev getD_get? := @getD_getElem?
@[deprecated getD_eq_getD_getElem? (since := "2025-02-12")] abbrev getD_eq_get? := @getD_eq_getD_getElem?
set_option linter.deprecated false in
@@ -4722,9 +4779,64 @@ theorem get?_eq_get?_toList (xs : Array α) (i : Nat) : xs.get? i = xs.toList.ge
set_option linter.deprecated false in
@[deprecated get!_eq_getD_getElem? (since := "2025-02-12")] abbrev get!_eq_get? := @get!_eq_getD_getElem?
@[deprecated getElem_set_self (since := "2025-01-17")]
theorem get_set_eq (xs : Array α) (i : Nat) (v : α) (h : i < xs.size) :
(xs.set i v h)[i]'(by simp [h]) = v := by
simp only [set, getElem_toList, List.getElem_set_self]
@[deprecated Array.getElem_toList (since := "2024-12-08")]
theorem getElem_eq_getElem_toList {xs : Array α} (h : i < xs.size) : xs[i] = xs.toList[i] := rfl
@[deprecated Array.getElem?_toList (since := "2024-12-08")]
theorem getElem?_eq_getElem?_toList (xs : Array α) (i : Nat) : xs[i]? = xs.toList[i]? := by
rw [getElem?_def]
split <;> simp_all
@[deprecated LawfulGetElem.getElem?_def (since := "2024-12-08")]
theorem getElem?_eq {xs : Array α} {i : Nat} :
xs[i]? = if h : i < xs.size then some xs[i] else none := by
rw [getElem?_def]
/-! ### map -/
@[deprecated "Use `toList_map` or `List.map_toArray` to characterize `Array.map`." (since := "2025-01-06")]
theorem map_induction (xs : Array α) (f : α β) (motive : Nat Prop) (h0 : motive 0)
(p : Fin xs.size β Prop) (hs : i, motive i.1 p i (f xs[i]) motive (i+1)) :
motive xs.size
eq : (xs.map f).size = xs.size, i h, p i, h ((xs.map f)[i]) := by
have t := foldl_induction (as := xs) (β := Array β)
(motive := fun i xs => motive i xs.size = i i h2, p i xs[i.1])
(init := #[]) (f := fun acc a => acc.push (f a)) ?_ ?_
obtain m, eq, w := t
· refine m, by simp, ?_
intro i h
simp only [eq] at w
specialize w i, h h
simpa using w
· exact h0, rfl, nofun
· intro i bs m, eq, w
refine ?_, ?_, ?_
· exact (hs _ m).2
· simp_all
· intro j h
simp at h
by_cases h' : j < size bs
· rw [getElem_push]
simp_all
· rw [getElem_push, dif_neg h']
simp only [show j = i by omega]
exact (hs _ m).1
set_option linter.deprecated false in
@[deprecated "Use `toList_map` or `List.map_toArray` to characterize `Array.map`." (since := "2025-01-06")]
theorem map_spec (xs : Array α) (f : α β) (p : Fin xs.size β Prop)
(hs : i, p i (f xs[i])) :
eq : (xs.map f).size = xs.size, i h, p i, h ((xs.map f)[i]) := by
simpa using map_induction xs f (fun _ => True) trivial p (by simp_all)
/-! ### set -/
@[deprecated getElem?_set_self (since := "2025-02-27")] abbrev get?_set_eq := @getElem?_set_self
@[deprecated getElem?_set_eq (since := "2025-02-27")] abbrev get?_set_eq := @getElem?_set_self
@[deprecated getElem?_set_ne (since := "2025-02-27")] abbrev get?_set_ne := @getElem?_set_ne
@[deprecated getElem?_set (since := "2025-02-27")] abbrev get?_set := @getElem?_set
@[deprecated get_set (since := "2025-02-27")] abbrev get_set := @getElem_set

View File

@@ -60,7 +60,7 @@ theorem mapFinIdx_spec {xs : Array α} {f : (i : Nat) → α → (h : i < xs.siz
@[simp, grind =] theorem size_zipIdx {xs : Array α} {k : Nat} : (xs.zipIdx k).size = xs.size :=
Array.size_mapFinIdx
@[deprecated size_zipIdx (since := "2025-01-21")] abbrev size_zipWithIndex := @size_zipIdx
@[simp, grind =] theorem getElem_mapFinIdx {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} {i : Nat}
(h : i < (xs.mapFinIdx f).size) :
@@ -132,20 +132,23 @@ namespace Array
(xs.zipIdx k)[i] = (xs[i]'(by simp_all), k + i) := by
simp [zipIdx]
@[deprecated getElem_zipIdx (since := "2025-01-21")]
abbrev getElem_zipWithIndex := @getElem_zipIdx
@[simp, grind =] theorem zipIdx_toArray {l : List α} {k : Nat} :
l.toArray.zipIdx k = (l.zipIdx k).toArray := by
ext i hi₁ hi₂ <;> simp
@[deprecated zipIdx_toArray (since := "2025-01-21")]
abbrev zipWithIndex_toArray := @zipIdx_toArray
@[simp, grind =] theorem toList_zipIdx {xs : Array α} {k : Nat} :
(xs.zipIdx k).toList = xs.toList.zipIdx k := by
rcases xs with xs
simp
@[deprecated toList_zipIdx (since := "2025-01-21")]
abbrev toList_zipWithIndex := @toList_zipIdx
theorem mk_mem_zipIdx_iff_le_and_getElem?_sub {k i : Nat} {x : α} {xs : Array α} :
(x, i) xs.zipIdx k k i xs[i - k]? = some x := by
@@ -170,7 +173,11 @@ theorem mem_zipIdx_iff_getElem? {x : α × Nat} {xs : Array α} :
x xs.zipIdx xs[x.2]? = some x.1 := by
rw [mk_mem_zipIdx_iff_getElem?]
@[deprecated mk_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
abbrev mk_mem_zipWithIndex_iff_getElem? := @mk_mem_zipIdx_iff_getElem?
@[deprecated mem_zipIdx_iff_getElem? (since := "2025-01-21")]
abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
/-! ### mapFinIdx -/
@@ -215,7 +222,8 @@ theorem mapFinIdx_eq_zipIdx_map {xs : Array α} {f : (i : Nat) → α → (h : i
f i x (by simp [mk_mem_zipIdx_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
ext <;> simp
@[deprecated mapFinIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapFinIdx_eq_zipWithIndex_map := @mapFinIdx_eq_zipIdx_map
@[simp]
theorem mapFinIdx_eq_empty_iff {xs : Array α} {f : (i : Nat) α (h : i < xs.size) β} :
@@ -324,7 +332,8 @@ theorem mapIdx_eq_zipIdx_map {xs : Array α} {f : Nat → α → β} :
xs.mapIdx f = xs.zipIdx.map fun a, i => f i a := by
ext <;> simp
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
@[grind =]
theorem mapIdx_append {xs ys : Array α} :

View File

@@ -5842,8 +5842,13 @@ set_option linter.missingDocs false
@[deprecated toFin_uShiftRight (since := "2025-02-18")]
abbrev toFin_uShiftRight := @toFin_ushiftRight
@[deprecated signExtend_eq_setWidth_of_msb_false (since := "2024-12-08")]
abbrev signExtend_eq_not_setWidth_not_of_msb_false := @signExtend_eq_setWidth_of_msb_false
@[deprecated replicate_zero (since := "2025-01-08")]
abbrev replicate_zero_eq := @replicate_zero
@[deprecated replicate_succ (since := "2025-01-08")]
abbrev replicate_succ_eq := @replicate_succ
end BitVec

View File

@@ -699,13 +699,13 @@ but may be used locally.
/-! ### Proof by reflection support -/
@[expose] protected noncomputable def Bool.and' (a b : Bool) : Bool :=
protected noncomputable def Bool.and' (a b : Bool) : Bool :=
Bool.rec false b a
@[expose] protected noncomputable def Bool.or' (a b : Bool) : Bool :=
protected noncomputable def Bool.or' (a b : Bool) : Bool :=
Bool.rec b true a
@[expose] protected noncomputable def Bool.not' (a : Bool) : Bool :=
protected noncomputable def Bool.not' (a : Bool) : Bool :=
Bool.rec true false a
@[simp] theorem Bool.and'_eq_and (a b : Bool) : a.and' b = a.and b := by

View File

@@ -107,14 +107,14 @@ Fin.foldrM n f xₙ = do
subst w
rfl
private theorem foldlM_loop_lt [Monad m] (f : α Fin n m α) (x) (h : i < n) :
theorem foldlM_loop_lt [Monad m] (f : α Fin n m α) (x) (h : i < n) :
foldlM.loop n f x i = f x i, h >>= (foldlM.loop n f . (i+1)) := by
rw [foldlM.loop, dif_pos h]
private theorem foldlM_loop_eq [Monad m] (f : α Fin n m α) (x) : foldlM.loop n f x n = pure x := by
theorem foldlM_loop_eq [Monad m] (f : α Fin n m α) (x) : foldlM.loop n f x n = pure x := by
rw [foldlM.loop, dif_neg (Nat.lt_irrefl _)]
private theorem foldlM_loop [Monad m] (f : α Fin (n+1) m α) (x) (h : i < n+1) :
theorem foldlM_loop [Monad m] (f : α Fin (n+1) m α) (x) (h : i < n+1) :
foldlM.loop (n+1) f x i = f x i, h >>= (foldlM.loop n (fun x j => f x j.succ) . i) := by
if h' : i < n then
rw [foldlM_loop_lt _ _ h]
@@ -170,15 +170,15 @@ theorem foldlM_add [Monad m] [LawfulMonad m] (f : α → Fin (n + k) → m α) :
subst w
rfl
private theorem foldrM_loop_zero [Monad m] (f : Fin n α m α) (x) :
theorem foldrM_loop_zero [Monad m] (f : Fin n α m α) (x) :
foldrM.loop n f 0, Nat.zero_le _ x = pure x := by
rw [foldrM.loop]
private theorem foldrM_loop_succ [Monad m] (f : Fin n α m α) (x) (h : i < n) :
theorem foldrM_loop_succ [Monad m] (f : Fin n α m α) (x) (h : i < n) :
foldrM.loop n f i+1, h x = f i, h x >>= foldrM.loop n f i, Nat.le_of_lt h := by
rw [foldrM.loop]
private theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) (x) (h : i+1 n+1) :
theorem foldrM_loop [Monad m] [LawfulMonad m] (f : Fin (n+1) α m α) (x) (h : i+1 n+1) :
foldrM.loop (n+1) f i+1, h x =
foldrM.loop n (fun j => f j.succ) i, Nat.le_of_succ_le_succ h x >>= f 0 := by
induction i generalizing x with
@@ -228,14 +228,14 @@ theorem foldrM_add [Monad m] [LawfulMonad m] (f : Fin (n + k) → α → m α) :
subst w
rfl
private theorem foldl_loop_lt (f : α Fin n α) (x) (h : i < n) :
theorem foldl_loop_lt (f : α Fin n α) (x) (h : i < n) :
foldl.loop n f x i = foldl.loop n f (f x i, h) (i+1) := by
rw [foldl.loop, dif_pos h]
private theorem foldl_loop_eq (f : α Fin n α) (x) : foldl.loop n f x n = x := by
theorem foldl_loop_eq (f : α Fin n α) (x) : foldl.loop n f x n = x := by
rw [foldl.loop, dif_neg (Nat.lt_irrefl _)]
private theorem foldl_loop (f : α Fin (n+1) α) (x) (h : i < n+1) :
theorem foldl_loop (f : α Fin (n+1) α) (x) (h : i < n+1) :
foldl.loop (n+1) f x i = foldl.loop n (fun x j => f x j.succ) (f x i, h) i := by
if h' : i < n then
rw [foldl_loop_lt _ _ h]
@@ -285,15 +285,15 @@ theorem foldlM_pure [Monad m] [LawfulMonad m] {n} {f : α → Fin n → α} :
subst w
rfl
private theorem foldr_loop_zero (f : Fin n α α) (x) :
theorem foldr_loop_zero (f : Fin n α α) (x) :
foldr.loop n f 0 (Nat.zero_le _) x = x := by
rw [foldr.loop]
private theorem foldr_loop_succ (f : Fin n α α) (x) (h : i < n) :
theorem foldr_loop_succ (f : Fin n α α) (x) (h : i < n) :
foldr.loop n f (i+1) h x = foldr.loop n f i (Nat.le_of_lt h) (f i, h x) := by
rw [foldr.loop]
private theorem foldr_loop (f : Fin (n+1) α α) (x) (h : i+1 n+1) :
theorem foldr_loop (f : Fin (n+1) α α) (x) (h : i+1 n+1) :
foldr.loop (n+1) f (i+1) h x =
f 0 (foldr.loop n (fun j => f j.succ) i (Nat.le_of_succ_le_succ h) x) := by
induction i generalizing x with

View File

@@ -938,7 +938,7 @@ For the induction:
(reverseInduction zero succ (Fin.last n) : motive (Fin.last n)) = zero := by
rw [reverseInduction, reverseInduction.go]; simp
private theorem reverseInduction_castSucc_aux {n : Nat} {motive : Fin (n + 1) Sort _} {succ}
@[simp] theorem reverseInduction_castSucc_aux {n : Nat} {motive : Fin (n + 1) Sort _} {succ}
(i : Fin n) (j : Nat) (h) (h2 : i.1 < j) (zero : motive j, h) :
reverseInduction.go (motive := motive) succ i.castSucc j h (Nat.le_of_lt h2) zero =
succ i (reverseInduction.go succ i.succ j h h2 zero) := by

View File

@@ -314,7 +314,7 @@ Examples:
* `(0 : Int).natAbs = 0`
* `((-11 : Int).natAbs = 11`
-/
@[extern "lean_nat_abs", expose]
@[extern "lean_nat_abs"]
def natAbs (m : @& Int) : Nat :=
match m with
| ofNat m => m
@@ -405,25 +405,11 @@ instance : Min Int := minOfLe
instance : Max Int := maxOfLe
/-- Equality predicate for kernel reduction. -/
@[expose] protected noncomputable def beq' (a b : Int) : Bool :=
protected noncomputable def beq' (a b : Int) : Bool :=
Int.rec
(fun a => Int.rec (fun b => Nat.beq a b) (fun _ => false) b)
(fun a => Int.rec (fun _ => false) (fun b => Nat.beq a b) b) a
/-- `x ≤ y` for kernel reduction. -/
@[expose] protected noncomputable def ble' (a b : Int) : Bool :=
Int.rec
(fun a => Int.rec (fun b => Nat.ble a b) (fun _ => false) b)
(fun a => Int.rec (fun _ => true) (fun b => Nat.ble b a) b)
a
/-- `x < y` for kernel reduction. -/
@[expose] protected noncomputable def blt' (a b : Int) : Bool :=
Int.rec
(fun a => Int.rec (fun b => Nat.blt a b) (fun _ => false) b)
(fun a => Int.rec (fun _ => true) (fun b => Nat.blt b a) b)
a
end Int
/--

View File

@@ -69,18 +69,6 @@ theorem natCast_succ_pos (n : Nat) : 0 < (n.succ : Int) := natCast_pos.2 n.succ_
@[simp, norm_cast] theorem cast_id {n : Int} : Int.cast n = n := rfl
@[simp] theorem ble'_eq_true (a b : Int) : (Int.ble' a b = true) = (a b) := by
cases a <;> cases b <;> simp [Int.ble'] <;> omega
@[simp] theorem blt'_eq_true (a b : Int) : (Int.blt' a b = true) = (a < b) := by
cases a <;> cases b <;> simp [Int.blt'] <;> omega
@[simp] theorem ble'_eq_false (a b : Int) : (Int.ble' a b = false) = ¬(a b) := by
simp [ Bool.not_eq_true]
@[simp] theorem blt'_eq_false (a b : Int) : (Int.blt' a b = false) = ¬ (a < b) := by
simp [ Bool.not_eq_true]
/-! ### toNat -/
@[simp] theorem toNat_sub' (a : Int) (b : Nat) : (a - b).toNat = a.toNat - b := by

File diff suppressed because it is too large Load Diff

View File

@@ -242,7 +242,8 @@ instance instLT [LT α] : LT (List α) := ⟨List.lt⟩
instance decidableLT [DecidableEq α] [LT α] [DecidableLT α] (l₁ l₂ : List α) :
Decidable (l₁ < l₂) := decidableLex (· < ·) l₁ l₂
@[deprecated decidableLT (since := "2024-12-13"), inherit_doc decidableLT]
abbrev hasDecidableLt := @decidableLT
/--
Non-strict ordering of lists with respect to a strict ordering of their elements.
@@ -1721,8 +1722,14 @@ Examples:
-/
def idxOf [BEq α] (a : α) : List α Nat := findIdx (· == a)
/-- Returns the index of the first element equal to `a`, or the length of the list otherwise. -/
@[deprecated idxOf (since := "2025-01-29")] abbrev indexOf := @idxOf
@[simp] theorem idxOf_nil [BEq α] : ([] : List α).idxOf x = 0 := rfl
@[deprecated idxOf_nil (since := "2025-01-29")]
theorem indexOf_nil [BEq α] : ([] : List α).idxOf x = 0 := rfl
/-! ### findIdx? -/
/--
@@ -1753,6 +1760,10 @@ Examples:
-/
@[inline] def idxOf? [BEq α] (a : α) : List α Option Nat := findIdx? (· == a)
/-- Return the index of the first occurrence of `a` in the list. -/
@[deprecated idxOf? (since := "2025-01-29")]
abbrev indexOf? := @idxOf?
/-! ### findFinIdx? -/
/--
@@ -2108,6 +2119,22 @@ def range' : (start len : Nat) → (step : Nat := 1) → List Nat
| _, 0, _ => []
| s, n+1, step => s :: range' (s+step) n step
/-! ### iota -/
/--
`O(n)`. `iota n` is the numbers from `1` to `n` inclusive, in decreasing order.
* `iota 5 = [5, 4, 3, 2, 1]`
-/
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
def iota : Nat List Nat
| 0 => []
| m@(n+1) => m :: iota n
set_option linter.deprecated false in
@[simp] theorem iota_zero : iota 0 = [] := rfl
set_option linter.deprecated false in
@[simp] theorem iota_succ : iota (i+1) = (i+1) :: iota i := rfl
/-! ### zipIdx -/
/--
@@ -2126,6 +2153,38 @@ def zipIdx : (l : List α) → (n : Nat := 0) → List (α × Nat)
@[simp] theorem zipIdx_nil : ([] : List α).zipIdx i = [] := rfl
@[simp] theorem zipIdx_cons : (a::as).zipIdx i = (a, i) :: as.zipIdx (i+1) := rfl
/-! ### enumFrom -/
/--
`O(|l|)`. `enumFrom n l` is like `enum` but it allows you to specify the initial index.
* `enumFrom 5 [a, b, c] = [(5, a), (6, b), (7, c)]`
-/
@[deprecated "Use `zipIdx` instead; note the signature change." (since := "2025-01-21")]
def enumFrom : Nat List α List (Nat × α)
| _, [] => nil
| n, x :: xs => (n, x) :: enumFrom (n + 1) xs
set_option linter.deprecated false in
@[deprecated zipIdx_nil (since := "2025-01-21"), simp]
theorem enumFrom_nil : ([] : List α).enumFrom i = [] := rfl
set_option linter.deprecated false in
@[deprecated zipIdx_cons (since := "2025-01-21"), simp]
theorem enumFrom_cons : (a::as).enumFrom i = (i, a) :: as.enumFrom (i+1) := rfl
/-! ### enum -/
set_option linter.deprecated false in
/--
`O(|l|)`. `enum l` pairs up each element with its index in the list.
* `enum [a, b, c] = [(0, a), (1, b), (2, c)]`
-/
@[deprecated "Use `zipIdx` instead; note the signature change." (since := "2025-01-21")]
def enum : List α List (Nat × α) := enumFrom 0
set_option linter.deprecated false in
@[deprecated zipIdx_nil (since := "2025-01-21"), simp]
theorem enum_nil : ([] : List α).enum = [] := rfl
/-! ## Minima and maxima -/
/-! ### min? -/
@@ -2545,6 +2604,25 @@ Examples:
exact go s n (m + 1)
exact (go s n 0).symm
/-! ### iota -/
/-- Tail-recursive version of `List.iota`. -/
@[deprecated "Use `List.range' 1 n` instead of `iota n`." (since := "2025-01-20")]
def iotaTR (n : Nat) : List Nat :=
let rec go : Nat List Nat List Nat
| 0, r => r.reverse
| m@(n+1), r => go n (m::r)
go n []
set_option linter.deprecated false in
@[csimp]
theorem iota_eq_iotaTR : @iota = @iotaTR :=
have aux (n : Nat) (r : List Nat) : iotaTR.go n r = r.reverse ++ iota n := by
induction n generalizing r with
| zero => simp [iota, iotaTR.go]
| succ n ih => simp [iota, iotaTR.go, ih, append_assoc]
funext fun n => by simp [iotaTR, aux]
/-! ## Other list operations -/
/-! ### intersperse -/

View File

@@ -57,9 +57,15 @@ theorem eraseP_of_forall_not {l : List α} (h : ∀ a, a ∈ l → ¬p a) : l.er
rintro x h' rfl
simp_all
@[deprecated eraseP_eq_nil_iff (since := "2025-01-30")]
abbrev eraseP_eq_nil := @eraseP_eq_nil_iff
theorem eraseP_ne_nil_iff {xs : List α} {p : α Bool} : xs.eraseP p [] xs [] x, p x xs [x] := by
simp
@[deprecated eraseP_ne_nil_iff (since := "2025-01-30")]
abbrev eraseP_ne_nil := @eraseP_ne_nil_iff
theorem exists_of_eraseP : {l : List α} {a} (_ : a l) (_ : p a),
a l₁ l₂, ( b l₁, ¬p b) p a l = l₁ ++ a :: l₂ l.eraseP p = l₁ ++ l₂
| b :: l, _, al, pa =>
@@ -346,11 +352,17 @@ theorem erase_eq_eraseP [LawfulBEq α] (a : α) : ∀ (l : List α), l.erase a =
rw [erase_eq_eraseP]
simp
@[deprecated erase_eq_nil_iff (since := "2025-01-30")]
abbrev erase_eq_nil := @erase_eq_nil_iff
theorem erase_ne_nil_iff [LawfulBEq α] {xs : List α} {a : α} :
xs.erase a [] xs [] xs [a] := by
rw [erase_eq_eraseP]
simp
@[deprecated erase_ne_nil_iff (since := "2025-01-30")]
abbrev erase_ne_nil := @erase_ne_nil_iff
theorem exists_erase_eq [LawfulBEq α] {a : α} {l : List α} (h : a l) :
l₁ l₂, a l₁ l = l₁ ++ a :: l₂ l.erase a = l₁ ++ l₂ := by
let _, l₁, l₂, h₁, e, h₂, h₃ := exists_of_eraseP h (beq_self_eq_true _)
@@ -570,7 +582,8 @@ theorem eraseIdx_eq_take_drop_succ :
| a::l, 0
| a::l, i + 1 => simp
@[deprecated eraseIdx_eq_nil_iff (since := "2025-01-30")]
abbrev eraseIdx_eq_nil := @eraseIdx_eq_nil_iff
theorem eraseIdx_ne_nil_iff {l : List α} {i : Nat} : eraseIdx l i [] 2 l.length (l.length = 1 i 0) := by
match l with
@@ -578,7 +591,8 @@ theorem eraseIdx_ne_nil_iff {l : List α} {i : Nat} : eraseIdx l i ≠ [] ↔ 2
| [a]
| a::b::l => simp
@[deprecated eraseIdx_ne_nil_iff (since := "2025-01-30")]
abbrev eraseIdx_ne_nil := @eraseIdx_ne_nil_iff
@[grind]
theorem eraseIdx_sublist : (l : List α) (k : Nat), eraseIdx l k <+ l
@@ -686,6 +700,7 @@ theorem erase_eq_eraseIdx_of_idxOf [BEq α] [LawfulBEq α]
rw [eq_comm, eraseIdx_eq_self]
exact Nat.le_of_eq (idxOf_eq_length h).symm
@[deprecated erase_eq_eraseIdx_of_idxOf (since := "2025-01-29")]
abbrev erase_eq_eraseIdx_of_indexOf := @erase_eq_eraseIdx_of_idxOf
end List

View File

@@ -1098,9 +1098,15 @@ theorem idxOf_cons [BEq α] :
dsimp [idxOf]
simp [findIdx_cons]
@[deprecated idxOf_cons (since := "2025-01-29")]
abbrev indexOf_cons := @idxOf_cons
@[simp] theorem idxOf_cons_self [BEq α] [ReflBEq α] {l : List α} : (a :: l).idxOf a = 0 := by
simp [idxOf_cons]
@[deprecated idxOf_cons_self (since := "2025-01-29")]
abbrev indexOf_cons_self := @idxOf_cons_self
@[grind =]
theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
(l₁ ++ l₂).idxOf a = if a l₁ then l₁.idxOf a else l₂.idxOf a + l₁.length := by
@@ -1111,7 +1117,8 @@ theorem idxOf_append [BEq α] [LawfulBEq α] {l₁ l₂ : List α} {a : α} :
· rw [if_neg]
simpa using h
@[deprecated idxOf_append (since := "2025-01-29")]
abbrev indexOf_append := @idxOf_append
theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a l) : l.idxOf a = l.length := by
induction l with
@@ -1121,7 +1128,8 @@ theorem idxOf_eq_length [BEq α] [LawfulBEq α] {l : List α} (h : a ∉ l) : l.
simp only [idxOf_cons, cond_eq_if, beq_iff_eq]
split <;> simp_all
@[deprecated idxOf_eq_length (since := "2025-01-29")]
abbrev indexOf_eq_length := @idxOf_eq_length
theorem idxOf_lt_length_of_mem [BEq α] [EquivBEq α] {l : List α} (h : a l) : l.idxOf a < l.length := by
induction l with
@@ -1151,7 +1159,8 @@ theorem idxOf_lt_length_iff [BEq α] [LawfulBEq α] {l : List α} {a : α} :
grind_pattern idxOf_lt_length_iff => l.idxOf a, l.length
@[deprecated idxOf_lt_length_of_mem (since := "2025-01-29")]
abbrev indexOf_lt_length := @idxOf_lt_length_of_mem
/-! ### finIdxOf?
@@ -1222,7 +1231,8 @@ The lemmas below should be made consistent with those for `findIdx?` (and proved
· rintro w x h rfl
contradiction
@[deprecated idxOf?_eq_none_iff (since := "2025-01-29")]
abbrev indexOf?_eq_none_iff := @idxOf?_eq_none_iff
@[simp, grind =]
theorem isSome_idxOf? [BEq α] [LawfulBEq α] {l : List α} {a : α} :

View File

@@ -98,7 +98,7 @@ Example:
[10, 14, 14]
```
-/
@[inline, expose] def filterMapTR (f : α Option β) (l : List α) : List β := go l #[] where
@[inline] def filterMapTR (f : α Option β) (l : List α) : List β := go l #[] where
/-- Auxiliary for `filterMap`: `filterMap.go f l = acc.toList ++ filterMap f l` -/
@[specialize] go : List α Array β List β
| [], acc => acc.toList
@@ -367,7 +367,7 @@ def modifyTR (l : List α) (i : Nat) (f : αα) : List α := go l i #[] whe
| a :: l, 0, acc => acc.toListAppend (f a :: l)
| a :: l, i+1, acc => go l i (acc.push a)
private theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify l i f
theorem modifyTR_go_eq : l i, modifyTR.go f l i acc = acc.toList ++ modify l i f
| [], i => by cases i <;> simp [modifyTR.go, modify]
| a :: l, 0 => by simp [modifyTR.go, modify]
| a :: l, i+1 => by simp [modifyTR.go, modify, modifyTR_go_eq l]
@@ -399,7 +399,7 @@ Examples:
| _, [], acc => acc.toList
| n+1, a :: l, acc => go n l (acc.push a)
private theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a
theorem insertIdxTR_go_eq : i l, insertIdxTR.go a i l acc = acc.toList ++ insertIdx l i a
| 0, l | _+1, [] => by simp [insertIdxTR.go, insertIdx]
| n+1, a :: l => by simp [insertIdxTR.go, insertIdx, insertIdxTR_go_eq n l]
@@ -564,7 +564,24 @@ def zipIdxTR (l : List α) (n : Nat := 0) : List (α × Nat) :=
/-! ### enumFrom -/
/-- Tail recursive version of `List.enumFrom`. -/
@[deprecated zipIdxTR (since := "2025-01-21")]
def enumFromTR (n : Nat) (l : List α) : List (Nat × α) :=
let as := l.toArray
(as.foldr (fun a (n, acc) => (n-1, (n-1, a) :: acc)) (n + as.size, [])).2
set_option linter.deprecated false in
@[deprecated zipIdx_eq_zipIdxTR (since := "2025-01-21"), csimp]
theorem enumFrom_eq_enumFromTR : @enumFrom = @enumFromTR := by
funext α n l; simp only [enumFromTR]
let f := fun (a : α) (n, acc) => (n-1, (n-1, a) :: acc)
let rec go : l n, l.foldr f (n + l.length, []) = (n, enumFrom n l)
| [], n => rfl
| a::as, n => by
rw [ show _ + as.length = n + (a::as).length from Nat.succ_add .., foldr, go as]
simp [enumFrom, f]
rw [ Array.foldr_toList]
simp +zetaDelta [go]
/-! ## Other list operations -/

View File

@@ -1586,7 +1586,9 @@ theorem filterMap_eq_cons_iff {l} {b} {bs} :
theorem not_mem_append {a : α} {s t : List α} (h₁ : a s) (h₂ : a t) : a s ++ t :=
mt mem_append.1 $ not_or.mpr h₁, h₂
@[deprecated mem_append (since := "2025-01-13")]
theorem mem_append_eq {a : α} {s t : List α} : (a s ++ t) = (a s a t) :=
propext mem_append
/--
See also `eq_append_cons_of_mem`, which proves a stronger version
@@ -1696,7 +1698,7 @@ theorem getLast_concat {a : α} : ∀ {l : List α}, getLast (l ++ [a]) (by simp
@[simp] theorem append_eq_nil_iff : p ++ q = [] p = [] q = [] := by
cases p <;> simp
@[deprecated append_eq_nil_iff (since := "2025-01-13")] abbrev append_eq_nil := @append_eq_nil_iff
theorem nil_eq_append_iff : [] = a ++ b a = [] b = [] := by
simp
@@ -2266,7 +2268,8 @@ theorem map_const' {l : List α} {b : β} : map (fun _ => b) l = replicate l.len
simp only [mem_append, mem_replicate, ne_eq]
rintro (-, rfl | _, rfl) <;> rfl
@[deprecated replicate_append_replicate (since := "2025-01-16")]
abbrev append_replicate_replicate := @replicate_append_replicate
theorem append_eq_replicate_iff {l₁ l₂ : List α} {a : α} :
l₁ ++ l₂ = replicate n a
@@ -2654,6 +2657,8 @@ theorem foldl_map_hom {g : α → β} {f : ααα} {f' : β → β →
· simp
· simp [*]
@[deprecated foldl_map_hom (since := "2025-01-20")] abbrev foldl_map' := @foldl_map_hom
theorem foldr_map_hom {g : α β} {f : α α α} {f' : β β β} {a : α} {l : List α}
(h : x y, f' (g x) (g y) = g (f x y)) :
(l.map g).foldr f' (g a) = g (l.foldr f a) := by
@@ -2661,6 +2666,8 @@ theorem foldr_map_hom {g : α → β} {f : ααα} {f' : β → β →
· simp
· simp [*]
@[deprecated foldr_map_hom (since := "2025-01-20")] abbrev foldr_map' := @foldr_map_hom
@[simp] theorem foldrM_append [Monad m] [LawfulMonad m] {f : α β m β} {b : β} {l l' : List α} :
(l ++ l').foldrM f b = l'.foldrM f b >>= l.foldrM f := by
induction l <;> simp [*]
@@ -3728,6 +3735,12 @@ theorem mem_iff_get? {a} {l : List α} : a ∈ l ↔ ∃ n, l.get? n = some a :=
/-! ### Deprecations -/
@[deprecated _root_.isSome_getElem? (since := "2024-12-09")]
theorem isSome_getElem? {l : List α} {i : Nat} : l[i]?.isSome i < l.length := by
simp
@[deprecated _root_.isNone_getElem? (since := "2024-12-09")]
theorem isNone_getElem? {l : List α} {i : Nat} : l[i]?.isNone l.length i := by
simp
end List

View File

@@ -163,7 +163,8 @@ instance [LT α] [Trans (· < · : αα → Prop) (· < ·) (· < ·)] :
Trans (· < · : List α List α Prop) (· < ·) (· < ·) where
trans h₁ h₂ := List.lt_trans h₁ h₂
@[deprecated List.le_antisymm (since := "2024-12-13")]
protected abbrev lt_antisymm := @List.le_antisymm
protected theorem lt_of_le_of_lt [LT α]
[i₀ : Std.Irrefl (· < · : α α Prop)]

View File

@@ -182,7 +182,8 @@ theorem mapFinIdx_eq_zipIdx_map {l : List α} {f : (i : Nat) → α → (h : i <
f i x (by rw [mk_mem_zipIdx_iff_getElem?, getElem?_eq_some_iff] at m; exact m.1) := by
apply ext_getElem <;> simp
@[deprecated mapFinIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapFinIdx_eq_zipWithIndex_map := @mapFinIdx_eq_zipIdx_map
@[simp]
theorem mapFinIdx_eq_nil_iff {l : List α} {f : (i : Nat) α (h : i < l.length) β} :
@@ -219,7 +220,7 @@ theorem mapFinIdx_eq_cons_iff {l : List α} {b : β} {f : (i : Nat) → α → (
cases l with
| nil => simp
| cons x l' =>
simp only [mapFinIdx_cons, cons.injEq,
simp only [mapFinIdx_cons, cons.injEq,
]
constructor
· rintro rfl, rfl
@@ -383,7 +384,8 @@ theorem mapIdx_eq_zipIdx_map {l : List α} {f : Nat → α → β} :
simp only [getElem?_mapIdx, Option.map, getElem?_map, getElem?_zipIdx]
split <;> simp
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-21")]
abbrev mapIdx_eq_enum_map := @mapIdx_eq_zipIdx_map
@[simp, grind =]
theorem mapIdx_cons {l : List α} {a : α} :

View File

@@ -261,7 +261,13 @@ theorem foldrM_filter [Monad m] [LawfulMonad m] {p : α → Bool} {g : α → β
/-! ### forM -/
@[deprecated forM_nil (since := "2025-01-31")]
theorem forM_nil' [Monad m] : ([] : List α).forM f = (pure .unit : m PUnit) := rfl
@[deprecated forM_cons (since := "2025-01-31")]
theorem forM_cons' [Monad m] :
(a::as).forM f = (f a >>= fun _ => as.forM f : m PUnit) :=
List.forM_cons
@[simp, grind =] theorem forM_append [Monad m] [LawfulMonad m] {l₁ l₂ : List α} {f : α m PUnit} :
forM (l₁ ++ l₂) f = (do forM l₁ f; forM l₂ f) := by

View File

@@ -90,6 +90,9 @@ theorem map_sub_range' {a s : Nat} (h : a ≤ s) (n : Nat) :
rintro rfl
omega
@[deprecated range'_eq_singleton_iff (since := "2025-01-29")]
abbrev range'_eq_singleton := @range'_eq_singleton_iff
theorem range'_eq_append_iff : range' s n = xs ++ ys k, k n xs = range' s k ys = range' (s + k) (n - k) := by
induction n generalizing s xs ys with
| zero => simp
@@ -227,6 +230,152 @@ theorem count_range {a n} :
rw [range_eq_range', count_range_1']
simp
/-! ### iota -/
section
set_option linter.deprecated false
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_eq_reverse_range' : n : Nat, iota n = reverse (range' 1 n)
| 0 => rfl
| n + 1 => by simp [iota, range'_concat, iota_eq_reverse_range' n, reverse_append, Nat.add_comm]
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem length_iota (n : Nat) : length (iota n) = n := by simp [iota_eq_reverse_range']
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem iota_eq_nil {n : Nat} : iota n = [] n = 0 := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_ne_nil {n : Nat} : iota n [] n 0 := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem mem_iota {m n : Nat} : m iota n 0 < m m n := by
simp [iota_eq_reverse_range', Nat.add_comm, Nat.lt_succ]
omega
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem iota_inj : iota n = iota n' n = n' := by
constructor
· intro h
have h' := congrArg List.length h
simp at h'
exact h'
· rintro rfl
simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_eq_cons_iff : iota n = a :: xs n = a 0 < n xs = iota (n - 1) := by
simp [iota_eq_reverse_range']
simp [range'_eq_append_iff, reverse_eq_iff]
constructor
· rintro k, h, rfl, h'
rw [eq_comm, range'_eq_singleton] at h'
simp only [reverse_inj, range'_inj, or_true, and_true]
omega
· rintro rfl, h, rfl
refine n - 1, by simp, rfl, ?_
rw [eq_comm, range'_eq_singleton]
omega
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem iota_eq_append_iff : iota n = xs ++ ys k, k n xs = (range' (k + 1) (n - k)).reverse ys = iota k := by
simp only [iota_eq_reverse_range']
rw [reverse_eq_append_iff]
rw [range'_eq_append_iff]
simp only [reverse_eq_iff]
constructor
· rintro k, h, rfl, rfl
simp; omega
· rintro k, h, rfl, rfl
exact k, by simp; omega
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem pairwise_gt_iota (n : Nat) : Pairwise (· > ·) (iota n) := by
simpa only [iota_eq_reverse_range', pairwise_reverse] using pairwise_lt_range'
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem nodup_iota (n : Nat) : Nodup (iota n) :=
(pairwise_gt_iota n).imp Nat.ne_of_gt
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem head?_iota (n : Nat) : (iota n).head? = if n = 0 then none else some n := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem head_iota (n : Nat) (h) : (iota n).head h = n := by
cases n with
| zero => simp at h
| succ n => simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem tail_iota (n : Nat) : (iota n).tail = iota (n - 1) := by
cases n <;> simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem reverse_iota : reverse (iota n) = range' 1 n := by
induction n with
| zero => simp
| succ n ih =>
rw [iota_succ, reverse_cons, ih, range'_1_concat, Nat.add_comm]
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem getLast?_iota (n : Nat) : (iota n).getLast? = if n = 0 then none else some 1 := by
rw [getLast?_eq_head?_reverse]
simp [head?_range']
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem getLast_iota (n : Nat) (h) : (iota n).getLast h = 1 := by
rw [getLast_eq_head_reverse]
simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20")]
theorem find?_iota_eq_none {n : Nat} {p : Nat Bool} :
(iota n).find? p = none i, 0 < i i n !p i := by
simp
@[deprecated "Use `(List.range' 1 n).reverse` instead of `iota n`." (since := "2025-01-20"), simp]
theorem find?_iota_eq_some {n : Nat} {i : Nat} {p : Nat Bool} :
(iota n).find? p = some i p i i iota n j, i < j j n !p j := by
rw [find?_eq_some_iff_append]
simp only [iota_eq_reverse_range', reverse_eq_append_iff, reverse_cons, append_assoc, cons_append,
nil_append, Bool.not_eq_eq_eq_not, Bool.not_true, exists_and_right, mem_reverse, mem_range'_1,
and_congr_right_iff]
intro h
constructor
· rintro as, xs, h, h'
constructor
· replace h : i range' 1 n := by
rw [h]
exact mem_append_cons_self
simpa using h
· rw [range'_eq_append_iff] at h
simp [reverse_eq_iff] at h
obtain k, h₁, rfl, h₂ := h
rw [eq_comm, range'_eq_cons_iff, reverse_eq_iff] at h₂
obtain rfl, -, rfl := h₂
intro j j₁ j₂
apply h'
simp; omega
· rintro i₁, i₂, h
refine (range' (i+1) (n-i)).reverse, (range' 1 (i-1)).reverse, ?_, ?_
· simp [ range'_succ]
rw [range'_eq_append_iff]
refine i-1, ?_
constructor
· omega
· simp
omega
· simp
intros a a₁ a₂
apply h
· omega
· omega
end
/-! ### zipIdx -/
@[simp, grind =]
@@ -363,4 +512,237 @@ theorem zipIdx_eq_append_iff {l : List α} {k : Nat} :
refine l₁', l₂', range' k l₁'.length, range' (k + l₁'.length) l₂'.length, ?_
simp
/-! ### enumFrom -/
section
set_option linter.deprecated false
@[deprecated zipIdx_singleton (since := "2025-01-21"), simp]
theorem enumFrom_singleton (x : α) (n : Nat) : enumFrom n [x] = [(n, x)] :=
rfl
@[deprecated head?_zipIdx (since := "2025-01-21"), simp]
theorem head?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).head? = l.head?.map fun a => (n, a) := by
simp [head?_eq_getElem?]
@[deprecated getLast?_zipIdx (since := "2025-01-21"), simp]
theorem getLast?_enumFrom (n : Nat) (l : List α) :
(enumFrom n l).getLast? = l.getLast?.map fun a => (n + l.length - 1, a) := by
simp [getLast?_eq_getElem?]
cases l <;> simp
@[deprecated mk_add_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
theorem mk_add_mem_enumFrom_iff_getElem? {n i : Nat} {x : α} {l : List α} :
(n + i, x) enumFrom n l l[i]? = some x := by
simp [mem_iff_get?]
@[deprecated mk_mem_zipIdx_iff_le_and_getElem?_sub (since := "2025-01-21")]
theorem mk_mem_enumFrom_iff_le_and_getElem?_sub {n i : Nat} {x : α} {l : List α} :
(i, x) enumFrom n l n i l[i - n]? = some x := by
if h : n i then
rcases Nat.exists_eq_add_of_le h with i, rfl
simp [mk_add_mem_enumFrom_iff_getElem?, Nat.add_sub_cancel_left]
else
have : k, n + k i := by rintro k rfl; simp at h
simp [h, mem_iff_get?, this]
@[deprecated le_snd_of_mem_zipIdx (since := "2025-01-21")]
theorem le_fst_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
n x.1 :=
(mk_mem_enumFrom_iff_le_and_getElem?_sub.1 h).1
@[deprecated snd_lt_add_of_mem_zipIdx (since := "2025-01-21")]
theorem fst_lt_add_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.1 < n + length l := by
rcases mem_iff_get.1 h with i, rfl
simpa using i.isLt
@[deprecated map_zipIdx (since := "2025-01-21")]
theorem map_enumFrom (f : α β) (n : Nat) (l : List α) :
map (Prod.map id f) (enumFrom n l) = enumFrom n (map f l) := by
induction l generalizing n <;> simp_all
@[deprecated fst_mem_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_mem_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) : x.2 l :=
enumFrom_map_snd n l mem_map_of_mem h
@[deprecated fst_eq_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_eq_of_mem_enumFrom {x : Nat × α} {n : Nat} {l : List α} (h : x enumFrom n l) :
x.2 = l[x.1 - n]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) := by
induction l generalizing n with
| nil => cases h
| cons hd tl ih =>
cases h with
| head _ => simp
| tail h m =>
specialize ih m
have : x.1 - n = x.1 - (n + 1) + 1 := by
have := le_fst_of_mem_enumFrom m
omega
simp [this, ih]
@[deprecated mem_zipIdx (since := "2025-01-21")]
theorem mem_enumFrom {x : α} {i j : Nat} {xs : List α} (h : (i, x) xs.enumFrom j) :
j i i < j + xs.length
x = xs[i - j]'(by have := le_fst_of_mem_enumFrom h; have := fst_lt_add_of_mem_enumFrom h; omega) :=
le_fst_of_mem_enumFrom h, fst_lt_add_of_mem_enumFrom h, snd_eq_of_mem_enumFrom h
@[deprecated zipIdx_map (since := "2025-01-21")]
theorem enumFrom_map (n : Nat) (l : List α) (f : α β) :
enumFrom n (l.map f) = (enumFrom n l).map (Prod.map id f) := by
induction l with
| nil => rfl
| cons hd tl IH =>
rw [map_cons, enumFrom_cons', enumFrom_cons', map_cons, map_map, IH, map_map]
rfl
@[deprecated zipIdx_append (since := "2025-01-21")]
theorem enumFrom_append (xs ys : List α) (n : Nat) :
enumFrom n (xs ++ ys) = enumFrom n xs ++ enumFrom (n + xs.length) ys := by
induction xs generalizing ys n with
| nil => simp
| cons x xs IH =>
rw [cons_append, enumFrom_cons, IH, cons_append, enumFrom_cons, length, Nat.add_right_comm,
Nat.add_assoc]
@[deprecated zipIdx_eq_cons_iff (since := "2025-01-21")]
theorem enumFrom_eq_cons_iff {l : List α} {n : Nat} :
l.enumFrom n = x :: l' a as, l = a :: as x = (n, a) l' = enumFrom (n + 1) as := by
rw [enumFrom_eq_zip_range', zip_eq_cons_iff]
constructor
· rintro l₁, l₂, h, rfl, rfl
rw [range'_eq_cons_iff] at h
obtain rfl, -, rfl := h
exact x.2, l₂, by simp [enumFrom_eq_zip_range']
· rintro a, as, rfl, rfl, rfl
refine range' (n+1) as.length, as, ?_
simp [enumFrom_eq_zip_range', range'_succ]
@[deprecated zipIdx_eq_append_iff (since := "2025-01-21")]
theorem enumFrom_eq_append_iff {l : List α} {n : Nat} :
l.enumFrom n = l₁ ++ l₂
l₁' l₂', l = l₁' ++ l₂' l₁ = l₁'.enumFrom n l₂ = l₂'.enumFrom (n + l₁'.length) := by
rw [enumFrom_eq_zip_range', zip_eq_append_iff]
constructor
· rintro ws, xs, ys, zs, h, h', rfl, rfl, rfl
rw [range'_eq_append_iff] at h'
obtain k, -, rfl, rfl := h'
simp only [length_range'] at h
obtain rfl := h
refine ys, zs, rfl, ?_
simp only [enumFrom_eq_zip_range', length_append, true_and]
congr
omega
· rintro l₁', l₂', rfl, rfl, rfl
simp only [enumFrom_eq_zip_range']
refine range' n l₁'.length, range' (n + l₁'.length) l₂'.length, l₁', l₂', ?_
simp
end
/-! ### enum -/
section
set_option linter.deprecated false
@[deprecated zipIdx_eq_nil_iff (since := "2025-01-21"), simp]
theorem enum_eq_nil_iff {l : List α} : List.enum l = [] l = [] := enumFrom_eq_nil
@[deprecated zipIdx_singleton (since := "2025-01-21"), simp]
theorem enum_singleton (x : α) : enum [x] = [(0, x)] := rfl
@[deprecated length_zipIdx (since := "2025-01-21"), simp]
theorem enum_length : (enum l).length = l.length :=
enumFrom_length
@[deprecated getElem?_zipIdx (since := "2025-01-21"), simp]
theorem getElem?_enum (l : List α) (i : Nat) : (enum l)[i]? = l[i]?.map fun a => (i, a) := by
rw [enum, getElem?_enumFrom, Nat.zero_add]
@[deprecated getElem_zipIdx (since := "2025-01-21"), simp]
theorem getElem_enum (l : List α) (i : Nat) (h : i < l.enum.length) :
l.enum[i] = (i, l[i]'(by simpa [enum_length] using h)) := by
simp [enum]
@[deprecated head?_zipIdx (since := "2025-01-21"), simp] theorem head?_enum (l : List α) :
l.enum.head? = l.head?.map fun a => (0, a) := by
simp [head?_eq_getElem?]
@[deprecated getLast?_zipIdx (since := "2025-01-21"), simp]
theorem getLast?_enum (l : List α) :
l.enum.getLast? = l.getLast?.map fun a => (l.length - 1, a) := by
simp [getLast?_eq_getElem?]
@[deprecated tail_zipIdx (since := "2025-01-21"), simp]
theorem tail_enum (l : List α) : (enum l).tail = enumFrom 1 l.tail := by
simp [enum]
@[deprecated mk_mem_zipIdx_iff_getElem? (since := "2025-01-21")]
theorem mk_mem_enum_iff_getElem? {i : Nat} {x : α} {l : List α} : (i, x) enum l l[i]? = some x := by
simp [enum, mk_mem_enumFrom_iff_le_and_getElem?_sub]
@[deprecated mem_zipIdx_iff_getElem? (since := "2025-01-21")]
theorem mem_enum_iff_getElem? {x : Nat × α} {l : List α} : x enum l l[x.1]? = some x.2 :=
mk_mem_enum_iff_getElem?
@[deprecated snd_lt_of_mem_zipIdx (since := "2025-01-21")]
theorem fst_lt_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.1 < length l := by
simpa using fst_lt_add_of_mem_enumFrom h
@[deprecated fst_mem_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_mem_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) : x.2 l :=
snd_mem_of_mem_enumFrom h
@[deprecated fst_eq_of_mem_zipIdx (since := "2025-01-21")]
theorem snd_eq_of_mem_enum {x : Nat × α} {l : List α} (h : x enum l) :
x.2 = l[x.1]'(fst_lt_of_mem_enum h) :=
snd_eq_of_mem_enumFrom h
@[deprecated mem_zipIdx (since := "2025-01-21")]
theorem mem_enum {x : α} {i : Nat} {xs : List α} (h : (i, x) xs.enum) :
i < xs.length x = xs[i]'(fst_lt_of_mem_enum h) :=
by simpa using mem_enumFrom h
@[deprecated map_zipIdx (since := "2025-01-21")]
theorem map_enum (f : α β) (l : List α) : map (Prod.map id f) (enum l) = enum (map f l) :=
map_enumFrom f 0 l
@[deprecated zipIdx_map_snd (since := "2025-01-21"), simp]
theorem enum_map_fst (l : List α) : map Prod.fst (enum l) = range l.length := by
simp only [enum, enumFrom_map_fst, range_eq_range']
@[deprecated zipIdx_map_fst (since := "2025-01-21"), simp]
theorem enum_map_snd (l : List α) : map Prod.snd (enum l) = l :=
enumFrom_map_snd _ _
@[deprecated zipIdx_map (since := "2025-01-21")]
theorem enum_map (l : List α) (f : α β) : (l.map f).enum = l.enum.map (Prod.map id f) :=
enumFrom_map _ _ _
@[deprecated zipIdx_append (since := "2025-01-21")]
theorem enum_append (xs ys : List α) : enum (xs ++ ys) = enum xs ++ enumFrom xs.length ys := by
simp [enum, enumFrom_append]
@[deprecated zipIdx_eq_zip_range' (since := "2025-01-21")]
theorem enum_eq_zip_range (l : List α) : l.enum = (range l.length).zip l :=
zip_of_prod (enum_map_fst _) (enum_map_snd _)
@[deprecated unzip_zipIdx_eq_prod (since := "2025-01-21"), simp]
theorem unzip_enum_eq_prod (l : List α) : l.enum.unzip = (range l.length, l) := by
simp only [enum_eq_zip_range, unzip_zip, length_range]
@[deprecated zipIdx_eq_cons_iff (since := "2025-01-21")]
theorem enum_eq_cons_iff {l : List α} :
l.enum = x :: l' a as, l = a :: as x = (0, a) l' = enumFrom 1 as := by
rw [enum, enumFrom_eq_cons_iff]
@[deprecated zipIdx_eq_append_iff (since := "2025-01-21")]
theorem enum_eq_append_iff {l : List α} :
l.enum = l₁ ++ l₂
l₁' l₂', l = l₁' ++ l₂' l₁ = l₁'.enum l₂ = l₂'.enumFrom l₁'.length := by
simp [enum, enumFrom_eq_append_iff]
end
end List

View File

@@ -39,9 +39,13 @@ theorem range'_succ {s n step} : range' s (n + 1) step = s :: range' (s + step)
@[simp] theorem range'_eq_nil_iff : range' s n step = [] n = 0 := by
rw [ length_eq_zero_iff, length_range']
@[deprecated range'_eq_nil_iff (since := "2025-01-29")] abbrev range'_eq_nil := @range'_eq_nil_iff
theorem range'_ne_nil_iff (s : Nat) {n step : Nat} : range' s n step [] n 0 := by
cases n <;> simp
@[deprecated range'_ne_nil_iff (since := "2025-01-29")] abbrev range'_ne_nil := @range'_ne_nil_iff
@[simp] theorem range'_zero : range' s 0 step = [] := by
simp
@@ -291,4 +295,107 @@ theorem zipIdx_eq_map_add {l : List α} {i : Nat} :
| nil => rfl
| cons _ _ ih => simp [ih (i := i + 1), zipIdx_succ, Nat.add_assoc, Nat.add_comm 1]
/-! ### enumFrom -/
section
set_option linter.deprecated false
@[deprecated zipIdx_eq_nil_iff (since := "2025-01-21"), simp]
theorem enumFrom_eq_nil {n : Nat} {l : List α} : List.enumFrom n l = [] l = [] := by
cases l <;> simp
@[deprecated length_zipIdx (since := "2025-01-21"), simp]
theorem enumFrom_length : {n} {l : List α}, (enumFrom n l).length = l.length
| _, [] => rfl
| _, _ :: _ => congrArg Nat.succ enumFrom_length
@[deprecated getElem?_zipIdx (since := "2025-01-21"), simp]
theorem getElem?_enumFrom :
i (l : List α) j, (enumFrom i l)[j]? = l[j]?.map fun a => (i + j, a)
| _, [], _ => rfl
| _, _ :: _, 0 => by simp
| n, _ :: l, m + 1 => by
simp only [enumFrom_cons, getElem?_cons_succ]
exact (getElem?_enumFrom (n + 1) l m).trans <| by rw [Nat.add_right_comm]; rfl
@[deprecated getElem_zipIdx (since := "2025-01-21"), simp]
theorem getElem_enumFrom (l : List α) (n) (i : Nat) (h : i < (l.enumFrom n).length) :
(l.enumFrom n)[i] = (n + i, l[i]'(by simpa [enumFrom_length] using h)) := by
simp only [enumFrom_length] at h
rw [getElem_eq_getElem?_get]
simp only [getElem?_enumFrom, getElem?_eq_getElem h]
simp
@[deprecated tail_zipIdx (since := "2025-01-21"), simp]
theorem tail_enumFrom (l : List α) (n : Nat) : (enumFrom n l).tail = enumFrom (n + 1) l.tail := by
induction l generalizing n with
| nil => simp
| cons _ l ih => simp [enumFrom_cons]
@[deprecated map_snd_add_zipIdx_eq_zipIdx (since := "2025-01-21"), simp]
theorem map_fst_add_enumFrom_eq_enumFrom (l : List α) (n k : Nat) :
map (Prod.map (· + n) id) (enumFrom k l) = enumFrom (n + k) l :=
ext_getElem? fun i by simp [Nat.add_comm, Nat.add_left_comm]; rfl
@[deprecated map_snd_add_zipIdx_eq_zipIdx (since := "2025-01-21"), simp]
theorem map_fst_add_enum_eq_enumFrom (l : List α) (n : Nat) :
map (Prod.map (· + n) id) (enum l) = enumFrom n l :=
map_fst_add_enumFrom_eq_enumFrom l _ _
@[deprecated zipIdx_cons' (since := "2025-01-21"), simp]
theorem enumFrom_cons' (n : Nat) (x : α) (xs : List α) :
enumFrom n (x :: xs) = (n, x) :: (enumFrom n xs).map (Prod.map (· + 1) id) := by
rw [enumFrom_cons, Nat.add_comm, map_fst_add_enumFrom_eq_enumFrom]
@[deprecated zipIdx_map_snd (since := "2025-01-21"), simp]
theorem enumFrom_map_fst (n) :
(l : List α), map Prod.fst (enumFrom n l) = range' n l.length
| [] => rfl
| _ :: _ => congrArg (cons _) (enumFrom_map_fst _ _)
@[deprecated zipIdx_map_fst (since := "2025-01-21"), simp]
theorem enumFrom_map_snd : (n) (l : List α), map Prod.snd (enumFrom n l) = l
| _, [] => rfl
| _, _ :: _ => congrArg (cons _) (enumFrom_map_snd _ _)
@[deprecated zipIdx_eq_zip_range' (since := "2025-01-21")]
theorem enumFrom_eq_zip_range' (l : List α) {n : Nat} : l.enumFrom n = (range' n l.length).zip l :=
zip_of_prod (enumFrom_map_fst _ _) (enumFrom_map_snd _ _)
@[deprecated unzip_zipIdx_eq_prod (since := "2025-01-21"), simp]
theorem unzip_enumFrom_eq_prod (l : List α) {n : Nat} :
(l.enumFrom n).unzip = (range' n l.length, l) := by
simp only [enumFrom_eq_zip_range', unzip_zip, length_range']
end
/-! ### enum -/
section
set_option linter.deprecated false
@[deprecated zipIdx_cons (since := "2025-01-21")]
theorem enum_cons : (a::as).enum = (0, a) :: as.enumFrom 1 := rfl
@[deprecated zipIdx_cons (since := "2025-01-21")]
theorem enum_cons' (x : α) (xs : List α) :
enum (x :: xs) = (0, x) :: (enum xs).map (Prod.map (· + 1) id) :=
enumFrom_cons' _ _ _
@[deprecated "These are now both `l.zipIdx 0`" (since := "2025-01-21")]
theorem enum_eq_enumFrom {l : List α} : l.enum = l.enumFrom 0 := rfl
@[deprecated "Use the reverse direction of `map_snd_add_zipIdx_eq_zipIdx` instead" (since := "2025-01-21")]
theorem enumFrom_eq_map_enum (l : List α) (n : Nat) :
enumFrom n l = (enum l).map (Prod.map (· + n) id) := by
induction l generalizing n with
| nil => simp
| cons x xs ih =>
simp only [enumFrom_cons, ih, enum_cons, map_cons, Prod.map_apply, Nat.zero_add, id_eq, map_map,
cons.injEq, map_inj_left, Function.comp_apply, Prod.forall, Prod.mk.injEq, and_true, true_and]
intro a b _
exact (succ_add a n).symm
end
end List

View File

@@ -57,7 +57,7 @@ where go : List α → List α → List α → List α
else
go (x :: xs) ys (y :: acc)
private theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge l₁ l₂ le := by
theorem mergeTR_go_eq : mergeTR.go le l₁ l₂ acc = acc.reverse ++ merge l₁ l₂ le := by
induction l₁ generalizing l₂ acc with
| nil => simp [mergeTR.go, reverseAux_eq]
| cons x l₁ ih₁ =>
@@ -84,7 +84,7 @@ def splitRevAt (n : Nat) (l : List α) : List α × List α := go l n [] where
| x :: xs, n+1, acc => go xs n (x :: acc)
| xs, _, acc => (acc, xs)
private theorem splitRevAt_go (xs : List α) (i : Nat) (acc : List α) :
theorem splitRevAt_go (xs : List α) (i : Nat) (acc : List α) :
splitRevAt.go xs i acc = ((take i xs).reverse ++ acc, drop i xs) := by
induction xs generalizing i acc with
| nil => simp [splitRevAt.go]
@@ -172,7 +172,7 @@ theorem splitRevInTwo_snd (l : { l : List α // l.length = n }) :
(splitRevInTwo l).2 = (splitInTwo l).2.1, by simp; omega := by
simp only [splitRevInTwo, splitRevAt_eq, reverse_take, splitInTwo_snd]
private theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort l.1 le
theorem mergeSortTR_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR.run le l = mergeSort l.1 le
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR.run]
| n+2, a :: b :: l, h => by
@@ -189,7 +189,7 @@ theorem mergeSort_eq_mergeSortTR : @mergeSort = @mergeSortTR := by
-- This mutual block is unfortunately quite slow to elaborate.
set_option maxHeartbeats 400000 in
mutual
private theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort l.1 le
theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) mergeSortTR₂.run le l = mergeSort l.1 le
| 0, [], _
| 1, [a], _ => by simp [mergeSortTR₂.run]
| n+2, a :: b :: l, h => by
@@ -201,7 +201,7 @@ private theorem mergeSortTR₂_run_eq_mergeSort : {n : Nat} → (l : { l : List
rw [reverse_reverse]
termination_by n => n
private theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort l' le
theorem mergeSortTR₂_run'_eq_mergeSort : {n : Nat} (l : { l : List α // l.length = n }) (w : l' = l.1.reverse) mergeSortTR₂.run' le l = mergeSort l' le
| 0, [], _, w
| 1, [a], _, w => by simp_all [mergeSortTR₂.run']
| n+2, a :: b :: l, h, w => by

View File

@@ -359,7 +359,7 @@ where go : ∀ (i : Nat) (l : List α),
omega
termination_by _ l => l.length
@[deprecated mergeSort_zipIdx (since := "2025-01-21")] abbrev mergeSort_enum := @mergeSort_zipIdx
theorem mergeSort_cons {le : α α Bool}
(trans : (a b c : α), le a b le b c le a c)

View File

@@ -222,11 +222,11 @@ theorem forM_toArray [Monad m] (l : List α) (f : α → m PUnit) :
congr
ext1 (_|_) <;> simp [ih]
private theorem findSomeRevM?_find_toArray [Monad m] [LawfulMonad m] (f : α m (Option β)) (l : List α)
theorem findSomeRevM?_find_toArray [Monad m] [LawfulMonad m] (f : α m (Option β)) (l : List α)
(i : Nat) (h) :
findSomeRevM?.find f l.toArray i h = (l.take i).reverse.findSomeM? f := by
induction i generalizing l with
| zero => simp [Array.findSomeRevM?.find]
| zero => simp [Array.findSomeRevM?.find.eq_def]
| succ i ih =>
rw [size_toArray] at h
rw [Array.findSomeRevM?.find, take_succ, getElem?_eq_getElem (by omega)]
@@ -437,7 +437,7 @@ theorem zipWithMAux_toArray_zero {m : Type u → Type v} [Monad m] [LawfulMonad
Array.zip as.toArray bs.toArray = (List.zip as bs).toArray := by
simp [Array.zip, zipWith_toArray, zip]
private theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α Option β γ) (i : Nat) (xs : Array γ) :
theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option α Option β γ) (i : Nat) (xs : Array γ) :
zipWithAll.go f as.toArray bs.toArray i xs = xs ++ (List.zipWithAll f (as.drop i) (bs.drop i)).toArray := by
unfold zipWithAll.go
split <;> rename_i h
@@ -489,7 +489,7 @@ private theorem zipWithAll_go_toArray (as : List α) (bs : List β) (f : Option
apply ext'
simp
private theorem takeWhile_go_succ (p : α Bool) (a : α) (l : List α) (i : Nat) :
theorem takeWhile_go_succ (p : α Bool) (a : α) (l : List α) (i : Nat) :
takeWhile.go p (a :: l).toArray (i+1) r = takeWhile.go p l.toArray i r := by
rw [takeWhile.go, takeWhile.go]
simp only [size_toArray, length_cons, Nat.add_lt_add_iff_right,
@@ -498,7 +498,7 @@ private theorem takeWhile_go_succ (p : α → Bool) (a : α) (l : List α) (i :
rw [takeWhile_go_succ]
rfl
private theorem takeWhile_go_toArray (p : α Bool) (l : List α) (i : Nat) :
theorem takeWhile_go_toArray (p : α Bool) (l : List α) (i : Nat) :
Array.takeWhile.go p l.toArray i r = r ++ (takeWhile p (l.drop i)).toArray := by
induction l generalizing i r with
| nil => simp [takeWhile.go]

View File

@@ -42,7 +42,7 @@ theorem zipWith_self {f : αα → δ} : ∀ {l : List α}, zipWith f l l =
| [] => rfl
| _ :: _ => congrArg _ zipWith_self
@[deprecated zipWith_self (since := "2025-01-29")] abbrev zipWith_same := @zipWith_self
/--
See also `getElem?_zipWith'` for a variant

View File

@@ -128,7 +128,7 @@ theorem fold_congr {α : Type u} {n m : Nat} (w : n = m)
subst m
rfl
private theorem foldTR_loop_congr {α : Type u} {n m : Nat} (w : n = m)
theorem foldTR_loop_congr {α : Type u} {n m : Nat} (w : n = m)
(f : (i : Nat) i < n α α) (j : Nat) (h : j n) (init : α) :
foldTR.loop n f j h init = foldTR.loop m (fun i h => f i (by omega)) j (by omega) init := by
subst m
@@ -154,7 +154,7 @@ theorem any_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bool) : a
subst m
rfl
private theorem anyTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) :
theorem anyTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) :
anyTR.loop n f j h = anyTR.loop m (fun i h => f i (by omega)) j (by omega) := by
subst m
rfl
@@ -179,7 +179,7 @@ theorem all_congr {n m : Nat} (w : n = m) (f : (i : Nat) → i < n → Bool) : a
subst m
rfl
private theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) : allTR.loop n f j h = allTR.loop m (fun i h => f i (by omega)) j (by omega) := by
theorem allTR_loop_congr {n m : Nat} (w : n = m) (f : (i : Nat) i < n Bool) (j : Nat) (h : j n) : allTR.loop n f j h = allTR.loop m (fun i h => f i (by omega)) j (by omega) := by
subst m
rfl

View File

@@ -85,11 +85,11 @@ theorem Int64.toInt_inj {x y : Int64} : x.toInt = y.toInt ↔ x = y := ⟨Int64.
theorem ISize.toInt.inj {x y : ISize} (h : x.toInt = y.toInt) : x = y := ISize.toBitVec.inj (BitVec.eq_of_toInt_eq h)
theorem ISize.toInt_inj {x y : ISize} : x.toInt = y.toInt x = y := ISize.toInt.inj, fun h => h rfl
@[simp, int_toBitVec] theorem Int8.toBitVec_neg (x : Int8) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_neg (x : Int16) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_neg (x : Int32) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_neg (x : Int64) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_neg (x : ISize) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp] theorem Int8.toBitVec_neg (x : Int8) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp] theorem Int16.toBitVec_neg (x : Int16) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp] theorem Int32.toBitVec_neg (x : Int32) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp] theorem Int64.toBitVec_neg (x : Int64) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp] theorem ISize.toBitVec_neg (x : ISize) : (-x).toBitVec = -x.toBitVec := (rfl)
@[simp] theorem Int8.toBitVec_zero : toBitVec 0 = 0#8 := (rfl)
@[simp] theorem Int16.toBitVec_zero : toBitVec 0 = 0#16 := (rfl)
@@ -103,11 +103,11 @@ theorem Int32.toBitVec_one : (1 : Int32).toBitVec = 1#32 := (rfl)
theorem Int64.toBitVec_one : (1 : Int64).toBitVec = 1#64 := (rfl)
theorem ISize.toBitVec_one : (1 : ISize).toBitVec = 1#System.Platform.numBits := (rfl)
@[simp, int_toBitVec] theorem Int8.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp] theorem Int8.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp] theorem Int16.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp] theorem Int32.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp] theorem Int64.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp] theorem ISize.toBitVec_ofInt (i : Int) : (ofInt i).toBitVec = BitVec.ofInt _ i := (rfl)
@[simp] protected theorem Int8.neg_zero : -(0 : Int8) = 0 := (rfl)
@[simp] protected theorem Int16.neg_zero : -(0 : Int16) = 0 := (rfl)
@@ -271,11 +271,11 @@ theorem ISize.toInt_maxValue : ISize.maxValue.toInt = 2 ^ (System.Platform.numBi
rw [toNatClampNeg, toInt_minValue]
cases System.Platform.numBits_eq <;> simp_all
@[simp, int_toBitVec] theorem UInt8.toBitVec_toInt8 (x : UInt8) : x.toInt8.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem UInt16.toBitVec_toInt16 (x : UInt16) : x.toInt16.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem UInt32.toBitVec_toInt32 (x : UInt32) : x.toInt32.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem UInt64.toBitVec_toInt64 (x : UInt64) : x.toInt64.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_toISize (x : USize) : x.toISize.toBitVec = x.toBitVec := (rfl)
@[simp] theorem UInt8.toBitVec_toInt8 (x : UInt8) : x.toInt8.toBitVec = x.toBitVec := (rfl)
@[simp] theorem UInt16.toBitVec_toInt16 (x : UInt16) : x.toInt16.toBitVec = x.toBitVec := (rfl)
@[simp] theorem UInt32.toBitVec_toInt32 (x : UInt32) : x.toInt32.toBitVec = x.toBitVec := (rfl)
@[simp] theorem UInt64.toBitVec_toInt64 (x : UInt64) : x.toInt64.toBitVec = x.toBitVec := (rfl)
@[simp] theorem USize.toBitVec_toISize (x : USize) : x.toISize.toBitVec = x.toBitVec := (rfl)
@[simp] theorem Int8.ofBitVec_uInt8ToBitVec (x : UInt8) : Int8.ofBitVec x.toBitVec = x.toInt8 := (rfl)
@[simp] theorem Int16.ofBitVec_uInt16ToBitVec (x : UInt16) : Int16.ofBitVec x.toBitVec = x.toInt16 := (rfl)
@@ -301,30 +301,30 @@ theorem ISize.toInt_maxValue : ISize.maxValue.toInt = 2 ^ (System.Platform.numBi
@[simp] theorem Int64.toInt_toBitVec (x : Int64) : x.toBitVec.toInt = x.toInt := (rfl)
@[simp] theorem ISize.toInt_toBitVec (x : ISize) : x.toBitVec.toInt = x.toInt := (rfl)
@[simp, int_toBitVec] theorem Int8.toBitVec_toInt16 (x : Int8) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp, int_toBitVec] theorem Int8.toBitVec_toInt32 (x : Int8) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp, int_toBitVec] theorem Int8.toBitVec_toInt64 (x : Int8) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
@[simp, int_toBitVec] theorem Int8.toBitVec_toISize (x : Int8) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp] theorem Int8.toBitVec_toInt16 (x : Int8) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp] theorem Int8.toBitVec_toInt32 (x : Int8) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp] theorem Int8.toBitVec_toInt64 (x : Int8) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
@[simp] theorem Int8.toBitVec_toISize (x : Int8) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_toInt8 (x : Int16) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_toInt32 (x : Int16) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_toInt64 (x : Int16) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_toISize (x : Int16) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp] theorem Int16.toBitVec_toInt8 (x : Int16) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp] theorem Int16.toBitVec_toInt32 (x : Int16) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp] theorem Int16.toBitVec_toInt64 (x : Int16) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
@[simp] theorem Int16.toBitVec_toISize (x : Int16) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_toInt8 (x : Int32) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_toInt16 (x : Int32) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_toInt64 (x : Int32) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_toISize (x : Int32) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp] theorem Int32.toBitVec_toInt8 (x : Int32) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp] theorem Int32.toBitVec_toInt16 (x : Int32) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp] theorem Int32.toBitVec_toInt64 (x : Int32) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
@[simp] theorem Int32.toBitVec_toISize (x : Int32) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_toInt8 (x : Int64) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_toInt16 (x : Int64) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_toInt32 (x : Int64) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_toISize (x : Int64) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp] theorem Int64.toBitVec_toInt8 (x : Int64) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp] theorem Int64.toBitVec_toInt16 (x : Int64) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp] theorem Int64.toBitVec_toInt32 (x : Int64) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp] theorem Int64.toBitVec_toISize (x : Int64) : x.toISize.toBitVec = x.toBitVec.signExtend System.Platform.numBits := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_toInt8 (x : ISize) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_toInt16 (x : ISize) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_toInt32 (x : ISize) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_toInt64 (x : ISize) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
@[simp] theorem ISize.toBitVec_toInt8 (x : ISize) : x.toInt8.toBitVec = x.toBitVec.signExtend 8 := (rfl)
@[simp] theorem ISize.toBitVec_toInt16 (x : ISize) : x.toInt16.toBitVec = x.toBitVec.signExtend 16 := (rfl)
@[simp] theorem ISize.toBitVec_toInt32 (x : ISize) : x.toInt32.toBitVec = x.toBitVec.signExtend 32 := (rfl)
@[simp] theorem ISize.toBitVec_toInt64 (x : ISize) : x.toInt64.toBitVec = x.toBitVec.signExtend 64 := (rfl)
theorem Int8.toInt_lt (x : Int8) : x.toInt < 2 ^ 7 := Int.lt_of_mul_lt_mul_left BitVec.two_mul_toInt_lt (by decide)
theorem Int8.le_toInt (x : Int8) : -2 ^ 7 x.toInt := Int.le_of_mul_le_mul_left BitVec.le_two_mul_toInt (by decide)
@@ -507,11 +507,11 @@ theorem Int32.toFin_toBitVec (x : Int32) : x.toBitVec.toFin = x.toUInt32.toFin :
theorem Int64.toFin_toBitVec (x : Int64) : x.toBitVec.toFin = x.toUInt64.toFin := (rfl)
theorem ISize.toFin_toBitVec (x : ISize) : x.toBitVec.toFin = x.toUSize.toFin := (rfl)
@[simp, int_toBitVec] theorem Int8.toBitVec_toUInt8 (x : Int8) : x.toUInt8.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_toUInt16 (x : Int16) : x.toUInt16.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_toUInt32 (x : Int32) : x.toUInt32.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_toUInt64 (x : Int64) : x.toUInt64.toBitVec = x.toBitVec := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_toUSize (x : ISize) : x.toUSize.toBitVec = x.toBitVec := (rfl)
@[simp] theorem Int8.toBitVec_toUInt8 (x : Int8) : x.toUInt8.toBitVec = x.toBitVec := (rfl)
@[simp] theorem Int16.toBitVec_toUInt16 (x : Int16) : x.toUInt16.toBitVec = x.toBitVec := (rfl)
@[simp] theorem Int32.toBitVec_toUInt32 (x : Int32) : x.toUInt32.toBitVec = x.toBitVec := (rfl)
@[simp] theorem Int64.toBitVec_toUInt64 (x : Int64) : x.toUInt64.toBitVec = x.toBitVec := (rfl)
@[simp] theorem ISize.toBitVec_toUSize (x : ISize) : x.toUSize.toBitVec = x.toBitVec := (rfl)
@[simp] theorem UInt8.ofBitVec_int8ToBitVec (x : Int8) : UInt8.ofBitVec x.toBitVec = x.toUInt8 := (rfl)
@[simp] theorem UInt16.ofBitVec_int16ToBitVec (x : Int16) : UInt16.ofBitVec x.toBitVec = x.toUInt16 := (rfl)
@@ -1001,11 +1001,11 @@ theorem USize.toISize_ofNatLT {n : Nat} (hn) : (USize.ofNatLT n hn).toISize = IS
@[simp] theorem UInt64.toInt64_ofBitVec (b) : (UInt64.ofBitVec b).toInt64 = Int64.ofBitVec b := (rfl)
@[simp] theorem USize.toISize_ofBitVec (b) : (USize.ofBitVec b).toISize = ISize.ofBitVec b := (rfl)
@[simp, int_toBitVec] theorem Int8.toBitVec_ofBitVec (b) : (Int8.ofBitVec b).toBitVec = b := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_ofBitVec (b) : (Int16.ofBitVec b).toBitVec = b := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_ofBitVec (b) : (Int32.ofBitVec b).toBitVec = b := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_ofBitVec (b) : (Int64.ofBitVec b).toBitVec = b := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_ofBitVec (b) : (ISize.ofBitVec b).toBitVec = b := (rfl)
@[simp] theorem Int8.toBitVec_ofBitVec (b) : (Int8.ofBitVec b).toBitVec = b := (rfl)
@[simp] theorem Int16.toBitVec_ofBitVec (b) : (Int16.ofBitVec b).toBitVec = b := (rfl)
@[simp] theorem Int32.toBitVec_ofBitVec (b) : (Int32.ofBitVec b).toBitVec = b := (rfl)
@[simp] theorem Int64.toBitVec_ofBitVec (b) : (Int64.ofBitVec b).toBitVec = b := (rfl)
@[simp] theorem ISize.toBitVec_ofBitVec (b) : (ISize.ofBitVec b).toBitVec = b := (rfl)
theorem Int8.toBitVec_ofIntTruncate {n : Int} (h₁ : Int8.minValue.toInt n) (h₂ : n Int8.maxValue.toInt) :
(Int8.ofIntTruncate n).toBitVec = BitVec.ofInt _ n := by
@@ -1281,11 +1281,11 @@ theorem Int64.toISize_ofIntTruncate {n : Int} (h₁ : -2 ^ 63 ≤ n) (h₂ : n <
(Int64.ofIntTruncate n).toISize = ISize.ofInt n := by
rw [ ofIntLE_eq_ofIntTruncate (h₁ := h₁) (h₂ := Int.le_of_lt_add_one h₂), toISize_ofIntLE]
@[simp, int_toBitVec] theorem Int8.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp, int_toBitVec] theorem Int16.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp, int_toBitVec] theorem Int32.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp, int_toBitVec] theorem Int64.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp, int_toBitVec] theorem ISize.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ :=
@[simp] theorem Int8.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp] theorem Int16.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp] theorem Int32.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp] theorem Int64.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ := (rfl)
@[simp] theorem ISize.toBitVec_minValue : minValue.toBitVec = BitVec.intMin _ :=
BitVec.eq_of_toInt_eq (by rw [toInt_toBitVec, toInt_minValue,
BitVec.toInt_intMin_of_pos (by cases System.Platform.numBits_eq <;> simp_all)])

View File

@@ -108,17 +108,9 @@ Examples:
* `#["red", "green", "blue"].toSubarray.popFront.foldl (· + ·.length) 0 = 9`
-/
@[inline]
def Subarray.foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Subarray α) : β :=
def foldl {α : Type u} {β : Type v} (f : β α β) (init : β) (as : Subarray α) : β :=
Slice.foldl f (init := init) as
/--
The implementation of `ForIn.forIn` for `Subarray`, which allows it to be used with `for` loops in
`do`-notation.
-/
@[inline]
def Subarray.forIn {α : Type u} {β : Type v} {m : Type v Type w} [Monad m] (s : Subarray α) (b : β) (f : α β m (ForInStep β)) : m β :=
ForIn.forIn s b f
namespace Array
/--

View File

@@ -36,7 +36,7 @@ instance : LT String :=
instance decidableLT (s₁ s₂ : @& String) : Decidable (s₁ < s₂) :=
List.decidableLT s₁.data s₂.data
@[deprecated decidableLT (since := "2024-12-13")] abbrev decLt := @decidableLT
/--
Non-strict inequality on strings, typically used via the `≤` operator.
@@ -652,7 +652,7 @@ Use `String.intercalate` to place a separator string between the strings in a li
Examples:
* `String.join ["gr", "ee", "n"] = "green"`
* `String.join ["b", "", "l", "", "ue"] = "blue"`
* `String.join ["b", "", "l", "", "ue"] = "red"`
* `String.join [] = ""`
-/
@[inline] def join (l : List String) : String :=

View File

@@ -473,34 +473,34 @@ theorem USize.size_dvd_uInt64Size : USize.size UInt64.size := by cases USize
@[simp] theorem USize.toFin_toUInt64 (n : USize) : n.toUInt64.toFin = n.toFin.castLE size_le_uint64Size := (rfl)
@[simp, int_toBitVec] theorem UInt16.toBitVec_toUInt8 (n : UInt16) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := (rfl)
@[simp, int_toBitVec] theorem UInt32.toBitVec_toUInt8 (n : UInt32) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := (rfl)
@[simp, int_toBitVec] theorem UInt64.toBitVec_toUInt8 (n : UInt64) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_toUInt8 (n : USize) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt16.toBitVec_toUInt8 (n : UInt16) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := (rfl)
@[simp] theorem UInt32.toBitVec_toUInt8 (n : UInt32) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := (rfl)
@[simp] theorem UInt64.toBitVec_toUInt8 (n : UInt64) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := (rfl)
@[simp] theorem USize.toBitVec_toUInt8 (n : USize) : n.toUInt8.toBitVec = n.toBitVec.setWidth 8 := BitVec.eq_of_toNat_eq (by simp)
@[simp, int_toBitVec] theorem UInt8.toBitVec_toUInt16 (n : UInt8) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := (rfl)
@[simp, int_toBitVec] theorem UInt32.toBitVec_toUInt16 (n : UInt32) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := (rfl)
@[simp, int_toBitVec] theorem UInt64.toBitVec_toUInt16 (n : UInt64) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_toUInt16 (n : USize) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt8.toBitVec_toUInt16 (n : UInt8) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := (rfl)
@[simp] theorem UInt32.toBitVec_toUInt16 (n : UInt32) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := (rfl)
@[simp] theorem UInt64.toBitVec_toUInt16 (n : UInt64) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := (rfl)
@[simp] theorem USize.toBitVec_toUInt16 (n : USize) : n.toUInt16.toBitVec = n.toBitVec.setWidth 16 := BitVec.eq_of_toNat_eq (by simp)
@[simp, int_toBitVec] theorem UInt8.toBitVec_toUInt32 (n : UInt8) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := (rfl)
@[simp, int_toBitVec] theorem UInt16.toBitVec_toUInt32 (n : UInt16) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := (rfl)
@[simp, int_toBitVec] theorem UInt64.toBitVec_toUInt32 (n : UInt64) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_toUInt32 (n : USize) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt8.toBitVec_toUInt32 (n : UInt8) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := (rfl)
@[simp] theorem UInt16.toBitVec_toUInt32 (n : UInt16) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := (rfl)
@[simp] theorem UInt64.toBitVec_toUInt32 (n : UInt64) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := (rfl)
@[simp] theorem USize.toBitVec_toUInt32 (n : USize) : n.toUInt32.toBitVec = n.toBitVec.setWidth 32 := BitVec.eq_of_toNat_eq (by simp)
@[simp, int_toBitVec] theorem UInt8.toBitVec_toUInt64 (n : UInt8) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := (rfl)
@[simp, int_toBitVec] theorem UInt16.toBitVec_toUInt64 (n : UInt16) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := (rfl)
@[simp, int_toBitVec] theorem UInt32.toBitVec_toUInt64 (n : UInt32) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_toUInt64 (n : USize) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 :=
@[simp] theorem UInt8.toBitVec_toUInt64 (n : UInt8) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := (rfl)
@[simp] theorem UInt16.toBitVec_toUInt64 (n : UInt16) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := (rfl)
@[simp] theorem UInt32.toBitVec_toUInt64 (n : UInt32) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 := (rfl)
@[simp] theorem USize.toBitVec_toUInt64 (n : USize) : n.toUInt64.toBitVec = n.toBitVec.setWidth 64 :=
BitVec.eq_of_toNat_eq (by simp)
@[simp, int_toBitVec] theorem UInt8.toBitVec_toUSize (n : UInt8) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
@[simp] theorem UInt8.toBitVec_toUSize (n : UInt8) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp)
@[simp, int_toBitVec] theorem UInt16.toBitVec_toUSize (n : UInt16) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
@[simp] theorem UInt16.toBitVec_toUSize (n : UInt16) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp)
@[simp, int_toBitVec] theorem UInt32.toBitVec_toUSize (n : UInt32) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
@[simp] theorem UInt32.toBitVec_toUSize (n : UInt32) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp)
@[simp, int_toBitVec] theorem UInt64.toBitVec_toUSize (n : UInt64) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
@[simp] theorem UInt64.toBitVec_toUSize (n : UInt64) : n.toUSize.toBitVec = n.toBitVec.setWidth System.Platform.numBits :=
BitVec.eq_of_toNat_eq (by simp)
@[simp] theorem UInt8.ofNatLT_toNat (n : UInt8) : UInt8.ofNatLT n.toNat n.toNat_lt = n := (rfl)
@@ -973,28 +973,28 @@ theorem USize.toFin_ofNatTruncate_of_le {n : Nat} (hn : USize.size ≤ n) :
(USize.ofNatTruncate n).toFin = USize.size - 1, by cases USize.size_eq <;> simp_all :=
Fin.val_inj.1 (by simp [toNat_ofNatTruncate_of_le hn])
@[simp, int_toBitVec] theorem UInt8.toBitVec_ofNatLT {n : Nat} (hn : n < UInt8.size) :
@[simp] theorem UInt8.toBitVec_ofNatLT {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := (rfl)
@[simp, int_toBitVec] theorem UInt16.toBitVec_ofNatLT {n : Nat} (hn : n < UInt16.size) :
@[simp] theorem UInt16.toBitVec_ofNatLT {n : Nat} (hn : n < UInt16.size) :
(UInt16.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := (rfl)
@[simp, int_toBitVec] theorem UInt32.toBitVec_ofNatLT {n : Nat} (hn : n < UInt32.size) :
@[simp] theorem UInt32.toBitVec_ofNatLT {n : Nat} (hn : n < UInt32.size) :
(UInt32.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := (rfl)
@[simp, int_toBitVec] theorem UInt64.toBitVec_ofNatLT {n : Nat} (hn : n < UInt64.size) :
@[simp] theorem UInt64.toBitVec_ofNatLT {n : Nat} (hn : n < UInt64.size) :
(UInt64.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_ofNatLT {n : Nat} (hn : n < USize.size) :
@[simp] theorem USize.toBitVec_ofNatLT {n : Nat} (hn : n < USize.size) :
(USize.ofNatLT n hn).toBitVec = BitVec.ofNatLT n hn := (rfl)
@[simp, int_toBitVec] theorem UInt8.toBitVec_ofFin (n : Fin UInt8.size) : (UInt8.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp, int_toBitVec] theorem UInt16.toBitVec_ofFin (n : Fin UInt16.size) : (UInt16.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp, int_toBitVec] theorem UInt32.toBitVec_ofFin (n : Fin UInt32.size) : (UInt32.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp, int_toBitVec] theorem UInt64.toBitVec_ofFin (n : Fin UInt64.size) : (UInt64.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_ofFin (n : Fin USize.size) : (USize.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp] theorem UInt8.toBitVec_ofFin (n : Fin UInt8.size) : (UInt8.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp] theorem UInt16.toBitVec_ofFin (n : Fin UInt16.size) : (UInt16.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp] theorem UInt32.toBitVec_ofFin (n : Fin UInt32.size) : (UInt32.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp] theorem UInt64.toBitVec_ofFin (n : Fin UInt64.size) : (UInt64.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp] theorem USize.toBitVec_ofFin (n : Fin USize.size) : (USize.ofFin n).toBitVec = BitVec.ofFin n := (rfl)
@[simp, int_toBitVec] theorem UInt8.toBitVec_ofBitVec (n) : (UInt8.ofBitVec n).toBitVec = n := (rfl)
@[simp, int_toBitVec] theorem UInt16.toBitVec_ofBitVec (n) : (UInt16.ofBitVec n).toBitVec = n := (rfl)
@[simp, int_toBitVec] theorem UInt32.toBitVec_ofBitVec (n) : (UInt32.ofBitVec n).toBitVec = n := (rfl)
@[simp, int_toBitVec] theorem UInt64.toBitVec_ofBitVec (n) : (UInt64.ofBitVec n).toBitVec = n := (rfl)
@[simp, int_toBitVec] theorem USize.toBitVec_ofBitVec (n) : (USize.ofBitVec n).toBitVec = n := (rfl)
@[simp] theorem UInt8.toBitVec_ofBitVec (n) : (UInt8.ofBitVec n).toBitVec = n := (rfl)
@[simp] theorem UInt16.toBitVec_ofBitVec (n) : (UInt16.ofBitVec n).toBitVec = n := (rfl)
@[simp] theorem UInt32.toBitVec_ofBitVec (n) : (UInt32.ofBitVec n).toBitVec = n := (rfl)
@[simp] theorem UInt64.toBitVec_ofBitVec (n) : (UInt64.ofBitVec n).toBitVec = n := (rfl)
@[simp] theorem USize.toBitVec_ofBitVec (n) : (USize.ofBitVec n).toBitVec = n := (rfl)
theorem UInt8.toBitVec_ofNatTruncate_of_lt {n : Nat} (hn : n < UInt8.size) :
(UInt8.ofNatTruncate n).toBitVec = BitVec.ofNatLT n hn :=

View File

@@ -310,7 +310,8 @@ def mapIdxM {α : Type u} {β : Type v} {m : Type v → Type w} [Monad m] (f : N
@[inline, expose] def zipIdx (xs : Vector α n) (k : Nat := 0) : Vector (α × Nat) n :=
xs.toArray.zipIdx k, by simp
@[deprecated zipIdx (since := "2025-01-21")]
abbrev zipWithIndex := @zipIdx
@[inline, expose] def zip (as : Vector α n) (bs : Vector β n) : Vector (α × β) n :=
as.toArray.zip bs.toArray, by simp
@@ -432,7 +433,8 @@ no element of the index matches the given value.
@[inline, expose] def finIdxOf? [BEq α] (xs : Vector α n) (x : α) : Option (Fin n) :=
(xs.toArray.finIdxOf? x).map (Fin.cast xs.size_toArray)
@[deprecated finIdxOf? (since := "2025-01-29")]
abbrev indexOf? := @finIdxOf?
/-- Finds the first index of a given value in a vector using a predicate. Returns `none` if the
no element of the index matches the given value. -/

View File

@@ -120,7 +120,8 @@ theorem toArray_mk {xs : Array α} (h : xs.size = n) : (Vector.mk xs h).toArray
@[simp] theorem findFinIdx?_mk {xs : Array α} (h : xs.size = n) (f : α Bool) :
(Vector.mk xs h).findFinIdx? f = (xs.findFinIdx? f).map (Fin.cast h) := rfl
@[deprecated finIdxOf?_mk (since := "2025-01-29")]
abbrev indexOf?_mk := @finIdxOf?_mk
@[simp] theorem findM?_mk [Monad m] {xs : Array α} (h : xs.size = n) (f : α m Bool) :
(Vector.mk xs h).findM? f = xs.findM? f := rfl
@@ -216,7 +217,8 @@ theorem toArray_mk {xs : Array α} (h : xs.size = n) : (Vector.mk xs h).toArray
@[simp] theorem zipIdx_mk {xs : Array α} (h : xs.size = n) (k : Nat := 0) :
(Vector.mk xs h).zipIdx k = Vector.mk (xs.zipIdx k) (by simp [h]) := rfl
@[deprecated zipIdx_mk (since := "2025-01-21")]
abbrev zipWithIndex_mk := @zipIdx_mk
@[simp] theorem mk_zipWith_mk {f : α β γ} {as : Array α} {bs : Array β}
(h : as.size = n) (h' : bs.size = n) :
@@ -321,7 +323,7 @@ abbrev toArray_mkEmpty := @toArray_emptyWithCapacity
xs.toArray.mapFinIdx (fun i a h => f i a (by simpa [xs.size_toArray] using h)) :=
rfl
private theorem toArray_mapM_go [Monad m] [LawfulMonad m] {f : α m β} {xs : Vector α n} {i} (h) {acc} :
theorem toArray_mapM_go [Monad m] [LawfulMonad m] {f : α m β} {xs : Vector α n} {i} (h) {acc} :
toArray <$> mapM.go f xs i h acc = Array.mapM.map f xs.toArray i acc.toArray := by
unfold mapM.go
unfold Array.mapM.map
@@ -1242,7 +1244,8 @@ instance [BEq α] [LawfulBEq α] (a : α) (as : Vector α n) : Decidable (a ∈
@[simp] theorem getElem_set_self {xs : Vector α n} {i : Nat} {x : α} (hi : i < n) :
(xs.set i x hi)[i] = x := by simp [getElem_set]
@[deprecated getElem_set_self (since := "2024-12-12")]
abbrev getElem_set_eq := @getElem_set_self
@[simp] theorem getElem_set_ne {xs : Vector α n} {x : α} (hi : i < n) (hj : j < n) (h : i j) :
(xs.set i x hi)[j] = xs[j] := by simp [getElem_set, h]
@@ -1303,7 +1306,8 @@ grind_pattern mem_or_eq_of_mem_set => a ∈ xs.set i b
@[simp] theorem getElem_setIfInBounds_self {xs : Vector α n} {x : α} (hi : i < n) :
(xs.setIfInBounds i x)[i] = x := by simp [getElem_setIfInBounds]
@[deprecated getElem_setIfInBounds_self (since := "2024-12-12")]
abbrev getElem_setIfInBounds_eq := @getElem_setIfInBounds_self
@[simp] theorem getElem_setIfInBounds_ne {xs : Vector α n} {x : α} (hj : j < n) (h : i j) :
(xs.setIfInBounds i x)[j] = xs[j] := by simp [getElem_setIfInBounds, h]

View File

@@ -97,7 +97,18 @@ theorem mem_zipIdx_iff_getElem? {x : α × Nat} {xs : Vector α n} :
rcases xs with xs, rfl
simp [Array.mem_zipIdx_iff_getElem?]
@[deprecated toList_zipIdx (since := "2025-01-27")]
abbrev toList_zipWithIndex := @toList_zipIdx
@[deprecated getElem_zipIdx (since := "2025-01-27")]
abbrev getElem_zipWithIndex := @getElem_zipIdx
@[deprecated mk_mem_zipIdx_iff_le_and_getElem?_sub (since := "2025-01-27")]
abbrev mk_mem_zipWithIndex_iff_le_and_getElem?_sub := @mk_mem_zipIdx_iff_le_and_getElem?_sub
@[deprecated mk_mem_zipIdx_iff_getElem? (since := "2025-01-27")]
abbrev mk_mem_zipWithIndex_iff_getElem? := @mk_mem_zipIdx_iff_getElem?
@[deprecated mem_zipIdx_iff_le_and_getElem?_sub (since := "2025-01-27")]
abbrev mem_zipWithIndex_iff_le_and_getElem?_sub := @mem_zipIdx_iff_le_and_getElem?_sub
@[deprecated mem_zipIdx_iff_getElem? (since := "2025-01-27")]
abbrev mem_zipWithIndex_iff_getElem? := @mem_zipIdx_iff_getElem?
/-! ### mapFinIdx -/
@@ -245,7 +256,8 @@ theorem mapIdx_eq_zipIdx_map {xs : Vector α n} {f : Nat → α → β} :
xs.mapIdx f = xs.zipIdx.map fun a, i => f i a := by
ext <;> simp
@[deprecated mapIdx_eq_zipIdx_map (since := "2025-01-27")]
abbrev mapIdx_eq_zipWithIndex_map := @mapIdx_eq_zipIdx_map
@[grind =]
theorem mapIdx_append {xs : Vector α n} {ys : Vector α m} :

View File

@@ -253,7 +253,8 @@ theorem getElem_of_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx
@[deprecated getElem?_eq_none_iff (since := "2025-02-17")]
abbrev getElem?_eq_none := @getElem?_eq_none_iff
@[deprecated getElem?_eq_none (since := "2024-12-11")]
abbrev isNone_getElem? := @getElem?_eq_none_iff
@[simp, grind =] theorem isSome_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?.isSome = dom c i := by

View File

@@ -78,9 +78,6 @@ theorem eq_eq_of_eq_true_right {a b : Prop} (h : b = True) : (a = b) = a := by s
theorem eq_congr {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = a₂) (h₂ : b₁ = b₂) : (a₁ = b₁) = (a₂ = b₂) := by simp [*]
theorem eq_congr' {α : Sort u} {a₁ b₁ a₂ b₂ : α} (h₁ : a₁ = b₂) (h₂ : b₁ = a₂) : (a₁ = b₁) = (a₂ = b₂) := by rw [h₁, h₂, Eq.comm (a := a₂)]
theorem heq_congr {α : Sort u} {β : Sort u} {a₁ b₁ : α} {a₂ b₂ : β} (h₁ : a₁ a₂) (h₂ : b₁ b₂) : (a₁ = b₁) = (a₂ = b₂) := by cases h₁; cases h₂; rfl
theorem heq_congr' {α : Sort u} {β : Sort u} {a₁ b₁ : α} {a₂ b₂ : β} (h₁ : a₁ b₂) (h₂ : b₁ a₂) : (a₁ = b₁) = (a₂ = b₂) := by cases h₁; cases h₂; rw [@Eq.comm _ a₁]
/-! Ne -/
theorem ne_of_ne_of_eq_left {α : Sort u} {a b c : α} (h₁ : a = b) (h₂ : b c) : a c := by simp [*]

View File

@@ -80,7 +80,7 @@ local instance {α} [IntModule α] : Std.Associative (· + · : ααα
local instance {α} [IntModule α] : Std.Commutative (· + · : α α α) where
comm := AddCommMonoid.add_comm
private theorem Poly.denote'_go_eq_denote {α} [IntModule α] (ctx : Context α) (p : Poly) (r : α) : denote'.go ctx r p = p.denote ctx + r := by
theorem Poly.denote'_go_eq_denote {α} [IntModule α] (ctx : Context α) (p : Poly) (r : α) : denote'.go ctx r p = p.denote ctx + r := by
induction r, p using denote'.go.induct ctx <;> simp [denote'.go, denote]
next ih => rw [ih]; ac_rfl
next ih => rw [ih]; ac_rfl
@@ -214,7 +214,7 @@ theorem Poly.denote_combine {α} [IntModule α] (ctx : Context α) (p₁ p₂ :
attribute [local simp] Poly.denote_combine
private theorem Expr.denote_toPoly'_go {α} [IntModule α] {k p} (ctx : Context α) (e : Expr)
theorem Expr.denote_toPoly'_go {α} [IntModule α] {k p} (ctx : Context α) (e : Expr)
: (toPoly'.go k e p).denote ctx = k * e.denote ctx + p.denote ctx := by
induction k, e using Expr.toPoly'.go.induct generalizing p <;> simp [toPoly'.go, denote, Poly.denote, *, zsmul_add]
next => ac_rfl

View File

@@ -10,7 +10,7 @@ public import Init.Grind.Ring.Basic
public import Init.Grind.Ordered.Ring
public import all Init.Data.AC
@[expose] public section
public section
namespace Lean.Grind.Ring
@@ -220,9 +220,9 @@ def npow (a : Q α) (n : Nat) : Q α :=
| 0 => natCast 1
| n+1 => mul (npow a n) a
theorem pow_zero (a : Q α) : npow a 0 = natCast 1 := rfl
private theorem pow_zero (a : Q α) : npow a 0 = natCast 1 := rfl
theorem pow_succ (a : Q α) (n : Nat) : npow a (n+1) = mul (npow a n) a := rfl
private theorem pow_succ (a : Q α) (n : Nat) : npow a (n+1) = mul (npow a n) a := rfl
def nsmul (n : Nat) (a : Q α) : Q α :=
mul (natCast n) a

View File

@@ -9,9 +9,9 @@ prelude
public import Init.Grind.Ring.Envelope
public import Init.Data.Hashable
public import Init.Data.RArray
public import Init.Grind.Ring.Poly
public import all Init.Grind.Ring.Poly
@[expose] public section
public section
namespace Lean.Grind.Ring.OfSemiring
/-!

View File

@@ -7,7 +7,6 @@ module
prelude
public import Init.Data.Nat.Lemmas
public import Init.Data.Int.LemmasAux
public import Init.Data.Hashable
public import all Init.Data.Ord
public import Init.Data.RArray
@@ -15,8 +14,7 @@ public import Init.Grind.Ring.Basic
public import Init.Grind.Ring.Field
public import Init.Grind.Ordered.Ring
public import Init.GrindInstances.Ring.Int
@[expose] public section
public section
namespace Lean.Grind
-- These are no longer global instances, so we need to turn them on here.
@@ -43,25 +41,23 @@ def Var.denote {α} (ctx : Context α) (v : Var) : α :=
ctx.get v
@[expose]
noncomputable def denoteInt {α} [Ring α] (k : Int) : α :=
Bool.rec
(OfNat.ofNat (α := α) k.natAbs)
(- OfNat.ofNat (α := α) k.natAbs)
(Int.blt' k 0)
def denoteInt {α} [Ring α] (k : Int) : α :=
bif k < 0 then
- OfNat.ofNat (α := α) k.natAbs
else
OfNat.ofNat (α := α) k.natAbs
@[expose]
noncomputable def Expr.denote {α} [Ring α] (ctx : Context α) (e : Expr) : α :=
Expr.rec
(fun k => denoteInt k)
(fun k => NatCast.natCast (R := α) k)
(fun k => IntCast.intCast (R := α) k)
(fun x => x.denote ctx)
(fun _ ih => - ih)
(fun _ _ ih₁ ih₂ => ih₁ + ih₂)
(fun _ _ ih₁ ih₂ => ih₁ - ih₂)
(fun _ _ ih₁ ih₂ => ih₁ * ih₂)
(fun _ k ih => ih ^ k)
e
def Expr.denote {α} [Ring α] (ctx : Context α) : Expr α
| .add a b => denote ctx a + denote ctx b
| .sub a b => denote ctx a - denote ctx b
| .mul a b => denote ctx a * denote ctx b
| .neg a => -denote ctx a
| .num k => denoteInt k
| .natCast k => NatCast.natCast (R := α) k
| .intCast k => IntCast.intCast (R := α) k
| .var v => v.denote ctx
| .pow a k => denote ctx a ^ k
structure Power where
x : Var
@@ -637,57 +633,9 @@ def Expr.toPoly : Expr → Poly
.num 1
else match a with
| .num n => .num (n^k)
| .intCast n => .num (n^k)
| .natCast n => .num (n^k)
| .var x => Poly.ofMon (.mult {x, k} .unit)
| _ => a.toPoly.pow k
@[expose] noncomputable def Expr.toPoly_k (e : Expr) : Poly :=
Expr.rec
(fun k => .num k) (fun k => .num k) (fun k => .num k)
(fun x => .ofVar x)
(fun _ ih => ih.mulConst_k (-1))
(fun _ _ ih₁ ih₂ => ih₁.combine_k ih₂)
(fun _ _ ih₁ ih₂ => ih₁.combine_k (ih₂.mulConst_k (-1)))
(fun _ _ ih₁ ih₂ => ih₁.mul ih₂)
(fun a k ih => Bool.rec
(Expr.rec (fun n => .num (n^k)) (fun n => .num (n^k)) (fun n => (.num (n^k)))
(fun x => .ofMon (.mult {x, k} .unit)) (fun _ _ => ih.pow k)
(fun _ _ _ _ => ih.pow k)
(fun _ _ _ _ => ih.pow k)
(fun _ _ _ _ => ih.pow k)
(fun _ _ _ => ih.pow k)
a)
(.num 1)
(k.beq 0))
e
@[simp] theorem Expr.toPoly_k_eq_toPoly (e : Expr) : e.toPoly_k = e.toPoly := by
induction e <;> simp only [toPoly, toPoly_k]
next a ih => rw [Poly.mulConst_k_eq_mulConst]; congr
case add => rw [ Poly.combine_k_eq_combine]; congr
case sub => rw [ Poly.combine_k_eq_combine, Poly.mulConst_k_eq_mulConst]; congr
case mul => congr
case pow a k ih =>
rw [cond_eq_if]; split
next h => rw [Nat.beq_eq_true_eq, Nat.beq_eq] at h; rw [h]
next h =>
rw [Nat.beq_eq_true_eq, Nat.beq_eq, Bool.not_eq_true] at h; rw [h]; dsimp only
show
(Expr.rec (fun n => .num (n^k)) (fun n => .num (n^k)) (fun n => (.num (n^k)))
(fun x => .ofMon (.mult {x, k} .unit)) (fun _ _ => a.toPoly_k.pow k)
(fun _ _ _ _ => a.toPoly_k.pow k)
(fun _ _ _ _ => a.toPoly_k.pow k)
(fun _ _ _ _ => a.toPoly_k.pow k)
(fun _ _ _ => a.toPoly_k.pow k)
a) = match a with
| num n => Poly.num (n ^ k)
| .intCast n => .num (n^k)
| .natCast n => .num (n^k)
| var x => Poly.ofMon (Mon.mult { x := x, k := k } Mon.unit)
| x => a.toPoly.pow k
cases a <;> try simp [*]
def Poly.normEq0 (p : Poly) (c : Nat) : Poly :=
match p with
| .num a =>
@@ -849,7 +797,7 @@ q₁*(lhs₁ - rhs₁) + ... + qₙ*(lhsₙ - rhsₙ)
```
-/
@[expose]
noncomputable def NullCert.denote {α} [CommRing α] (ctx : Context α) : NullCert α
def NullCert.denote {α} [CommRing α] (ctx : Context α) : NullCert α
| .empty => 0
| .add q lhs rhs nc => (q.denote ctx)*(lhs.denote ctx - rhs.denote ctx) + nc.denote ctx
@@ -892,9 +840,9 @@ open Ring hiding sub_eq_add_neg
open CommSemiring
theorem denoteInt_eq {α} [CommRing α] (k : Int) : denoteInt (α := α) k = k := by
simp [denoteInt] <;> cases h : k.blt' 0 <;> simp <;> simp at h
next h => rw [ofNat_eq_natCast, intCast_natCast, Int.eq_natAbs_of_nonneg h]
next h => rw [ofNat_eq_natCast, intCast_natCast, Ring.intCast_neg, Int.eq_neg_natAbs_of_nonpos (Int.le_of_lt h)]
simp [denoteInt, cond_eq_if] <;> split
next h => rw [ofNat_eq_natCast, intCast_natCast, intCast_neg, Int.eq_neg_natAbs_of_nonpos (Int.le_of_lt h)]
next h => rw [ofNat_eq_natCast, intCast_natCast, Int.eq_natAbs_of_nonneg (Int.le_of_not_gt h)]
theorem Power.denote_eq {α} [Semiring α] (ctx : Context α) (p : Power)
: p.denote ctx = p.x.denote ctx ^ p.k := by
@@ -1099,7 +1047,6 @@ theorem Expr.denote_toPoly {α} [CommRing α] (ctx : Context α) (e : Expr)
neg_mul, one_mul, sub_eq_add_neg, denoteInt_eq, *]
next => rw [Ring.intCast_natCast]
next a k h => simp at h; simp [h, Semiring.pow_zero]
next => rw [Ring.intCast_natCast]
next => simp [Poly.denote_ofMon, Mon.denote, Power.denote_eq, mul_one]
theorem Expr.eq_of_toPoly_eq {α} [CommRing α] (ctx : Context α) (a b : Expr) (h : a.toPoly == b.toPoly) : a.denote ctx = b.denote ctx := by
@@ -1370,7 +1317,7 @@ Theorems for stepwise proof-term construction
-/
@[expose]
noncomputable def core_cert (lhs rhs : Expr) (p : Poly) : Bool :=
(lhs.sub rhs).toPoly_k.beq' p
(lhs.sub rhs).toPoly.beq' p
theorem core {α} [CommRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p lhs.denote ctx = rhs.denote ctx p.denote ctx = 0 := by
@@ -1453,7 +1400,7 @@ theorem d_stepk {α} [CommRing α] (ctx : Context α) (k₁ : Int) (k : Int) (in
@[expose]
noncomputable def imp_1eq_cert (lhs rhs : Expr) (p₁ p₂ : Poly) : Bool :=
(lhs.sub rhs).toPoly_k.beq' p₁ |>.and' (p₂.beq' (.num 0))
(lhs.sub rhs).toPoly.beq' p₁ |>.and' (p₂.beq' (.num 0))
theorem imp_1eq {α} [CommRing α] (ctx : Context α) (lhs rhs : Expr) (p₁ p₂ : Poly)
: imp_1eq_cert lhs rhs p₁ p₂ (1:Int) * p₁.denote ctx = p₂.denote ctx lhs.denote ctx = rhs.denote ctx := by
@@ -1462,7 +1409,7 @@ theorem imp_1eq {α} [CommRing α] (ctx : Context α) (lhs rhs : Expr) (p₁ p
@[expose]
noncomputable def imp_keq_cert (lhs rhs : Expr) (k : Int) (p₁ p₂ : Poly) : Bool :=
!Int.beq' k 0 |>.and' ((lhs.sub rhs).toPoly_k.beq' p₁ |>.and' (p₂.beq' (.num 0)))
!Int.beq' k 0 |>.and' ((lhs.sub rhs).toPoly.beq' p₁ |>.and' (p₂.beq' (.num 0)))
theorem imp_keq {α} [CommRing α] (ctx : Context α) [NoNatZeroDivisors α] (k : Int) (lhs rhs : Expr) (p₁ p₂ : Poly)
: imp_keq_cert lhs rhs k p₁ p₂ k * p₁.denote ctx = p₂.denote ctx lhs.denote ctx = rhs.denote ctx := by
@@ -1649,14 +1596,14 @@ theorem not_lt_norm {α} [CommRing α] [LinearOrder α] [OrderedRing α] (ctx :
theorem not_le_norm' {α} [CommRing α] [Preorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p ¬ lhs.denote ctx rhs.denote ctx ¬ p.denoteAsIntModule ctx 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]; intro h
replace h : rhs.denote ctx + (lhs.denote ctx - rhs.denote ctx) _ := add_le_right (rhs.denote ctx) h
replace h := add_le_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp [add_zero] at h
contradiction
theorem not_lt_norm' {α} [CommRing α] [Preorder α] [OrderedRing α] (ctx : Context α) (lhs rhs : Expr) (p : Poly)
: core_cert lhs rhs p ¬ lhs.denote ctx < rhs.denote ctx ¬ p.denoteAsIntModule ctx < 0 := by
simp [core_cert, Poly.denoteAsIntModule_eq_denote]; intro _ h₁; subst p; simp [Expr.denote_toPoly, Expr.denote]; intro h
replace h : rhs.denote ctx + (lhs.denote ctx - rhs.denote ctx) < _ := add_lt_right (rhs.denote ctx) h
replace h := add_lt_right (rhs.denote ctx) h
rw [sub_eq_add_neg, add_left_comm, sub_eq_add_neg, sub_self] at h; simp [add_zero] at h
contradiction
@@ -1775,7 +1722,7 @@ theorem d_normEq0 {α} [CommRing α] (ctx : Context α) (k : Int) (c : Nat) (ini
intro h; rw [p₁.normEq0_eq] <;> assumption
@[expose] noncomputable def norm_int_cert (e : Expr) (p : Poly) : Bool :=
e.toPoly_k.beq' p
e.toPoly.beq' p
theorem norm_int (ctx : Context Int) (e : Expr) (p : Poly) : norm_int_cert e p e.denote ctx = p.denote' ctx := by
simp [norm_int_cert, Poly.denote'_eq_denote]; intro; subst p; simp [Expr.denote_toPoly]

View File

@@ -549,7 +549,7 @@ macro_rules
`($f $a)
/--
Haskell-like pipe operator `|>`. `x |> f` means the same as `f x`,
Haskell-like pipe operator `|>`. `x |> f` means the same as the same as `f x`,
and it chains such that `x |> f |> g` is interpreted as `g (f x)`.
-/
syntax:min term " |> " term:min1 : term

View File

@@ -9,6 +9,7 @@ prelude
public import Init.System.IO
public import Init.System.Platform
public import Init.System.Uri
public import Init.System.Mutex
public import Init.System.Promise
public section

View File

@@ -533,6 +533,13 @@ Waits for the task to finish, then returns its result.
@[extern "lean_io_wait"] opaque wait (t : Task α) : BaseIO α :=
return t.get
/--
Waits until any of the tasks in the list has finished, then return its result.
-/
@[extern "lean_io_wait_any"] opaque waitAny (tasks : @& List (Task α))
(h : tasks.length > 0 := by exact Nat.zero_lt_succ _) : BaseIO α :=
return tasks[0].get
/--
Returns the number of _heartbeats_ that have occurred during the current thread's execution. The
heartbeat count is the number of “small” memory allocations performed in a thread.

134
src/Init/System/Mutex.lean Normal file
View File

@@ -0,0 +1,134 @@
/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Gabriel Ebner
-/
module
prelude
public import Init.System.IO
public import Init.Control.StateRef
public section
set_option linter.deprecated false
namespace IO
private opaque BaseMutexImpl : NonemptyType.{0}
/--
Mutual exclusion primitive (a lock).
If you want to guard shared state, use `Mutex α` instead.
-/
@[deprecated "Use Std.BaseMutex from Std.Sync.Mutex instead" (since := "2024-12-02")]
def BaseMutex : Type := BaseMutexImpl.type
instance : Nonempty BaseMutex := by exact BaseMutexImpl.property
/-- Creates a new `BaseMutex`. -/
@[extern "lean_io_basemutex_new", deprecated "Use Std.BaseMutex.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque BaseMutex.new : BaseIO BaseMutex
/--
Locks a `BaseMutex`. Waits until no other thread has locked the mutex.
The current thread must not have already locked the mutex.
Reentrant locking is undefined behavior (inherited from the C++ implementation).
-/
@[extern "lean_io_basemutex_lock", deprecated "Use Std.BaseMutex.lock from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque BaseMutex.lock (mutex : @& BaseMutex) : BaseIO Unit
/--
Unlocks a `BaseMutex`.
The current thread must have already locked the mutex.
Unlocking an unlocked mutex is undefined behavior (inherited from the C++ implementation).
-/
@[extern "lean_io_basemutex_unlock", deprecated "Use Std.BaseMutex.unlock from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque BaseMutex.unlock (mutex : @& BaseMutex) : BaseIO Unit
private opaque CondvarImpl : NonemptyType.{0}
/-- Condition variable. -/
@[deprecated "Use Std.Condvar from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Condvar : Type := CondvarImpl.type
instance : Nonempty Condvar := by exact CondvarImpl.property
/-- Creates a new condition variable. -/
@[extern "lean_io_condvar_new", deprecated "Use Std.Condvar.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.new : BaseIO Condvar
/-- Waits until another thread calls `notifyOne` or `notifyAll`. -/
@[extern "lean_io_condvar_wait", deprecated "Use Std.Condvar.wait from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.wait (condvar : @& Condvar) (mutex : @& BaseMutex) : BaseIO Unit
/-- Wakes up a single other thread executing `wait`. -/
@[extern "lean_io_condvar_notify_one", deprecated "Use Std.Condvar.notifyOne from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.notifyOne (condvar : @& Condvar) : BaseIO Unit
/-- Wakes up all other threads executing `wait`. -/
@[extern "lean_io_condvar_notify_all", deprecated "Use Std.Condvar.notifyAll from Std.Sync.Mutex instead" (since := "2024-12-02")]
opaque Condvar.notifyAll (condvar : @& Condvar) : BaseIO Unit
/-- Waits on the condition variable until the predicate is true. -/
@[deprecated "Use Std.Condvar.waitUntil from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Condvar.waitUntil [Monad m] [MonadLift BaseIO m]
(condvar : Condvar) (mutex : BaseMutex) (pred : m Bool) : m Unit := do
while !( pred) do
condvar.wait mutex
/--
Mutual exclusion primitive (lock) guarding shared state of type `α`.
The type `Mutex α` is similar to `IO.Ref α`,
except that concurrent accesses are guarded by a mutex
instead of atomic pointer operations and busy-waiting.
-/
@[deprecated "Use Std.Mutex from Std.Sync.Mutex instead" (since := "2024-12-02")]
structure Mutex (α : Type) where private mk ::
private ref : IO.Ref α
mutex : BaseMutex
deriving Nonempty
instance : CoeOut (Mutex α) BaseMutex where coe := Mutex.mutex
/-- Creates a new mutex. -/
@[deprecated "Use Std.Mutex.new from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Mutex.new (a : α) : BaseIO (Mutex α) :=
return { ref := mkRef a, mutex := BaseMutex.new }
/--
`AtomicT α m` is the monad that can be atomically executed inside a `Mutex α`,
with outside monad `m`.
The action has access to the state `α` of the mutex (via `get` and `set`).
-/
@[deprecated "Use Std.AtomicT from Std.Sync.Mutex instead" (since := "2024-12-02")]
abbrev AtomicT := StateRefT' IO.RealWorld
/-- `mutex.atomically k` runs `k` with access to the mutex's state while locking the mutex. -/
@[deprecated "Use Std.Mutex.atomically from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Mutex.atomically [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
(mutex : Mutex α) (k : AtomicT α m β) : m β := do
try
mutex.mutex.lock
k mutex.ref
finally
mutex.mutex.unlock
/--
`mutex.atomicallyOnce condvar pred k` runs `k`,
waiting on `condvar` until `pred` returns true.
Both `k` and `pred` have access to the mutex's state.
-/
@[deprecated "Use Std.Mutex.atomicallyOnce from Std.Sync.Mutex instead" (since := "2024-12-02")]
def Mutex.atomicallyOnce [Monad m] [MonadLiftT BaseIO m] [MonadFinally m]
(mutex : Mutex α) (condvar : Condvar)
(pred : AtomicT α m Bool) (k : AtomicT α m β) : m β :=
let _ : MonadLift BaseIO (AtomicT α m) := liftM
mutex.atomically do
condvar.waitUntil mutex pred
k

View File

@@ -64,10 +64,8 @@ private opaque Option.getOrBlock! [Nonempty α] : Option αα
The result task of a `Promise`.
The task blocks until `Promise.resolve` is called. If the promise is dropped without ever being
resolved, evaluating the task will panic and, when not using fatal panics, block forever. As
`Promise.result!` is a pure value and thus the point of evaluation may not be known precisely, this
means that any promise on which `Promise.result!` *may* be evaluated *must* be resolved eventually.
When in doubt, always prefer `Promise.result?` to handle dropped promises explicitly.
resolved, evaluating the task will panic and, when not using fatal panics, block forever. Use
`Promise.result?` to handle this case explicitly.
-/
def Promise.result! (promise : @& Promise α) : Task α :=
let _ : Nonempty α := promise.h

View File

@@ -10,21 +10,9 @@ module
prelude
public import Init.Core
public import Init.Data.List.Basic
public import Init.System.Promise
public section
/--
Waits until any of the tasks in the list has finished, then return its result.
-/
@[noinline]
def IO.waitAny (tasks : @& List (Task α)) (h : tasks.length > 0 := by exact Nat.zero_lt_succ _) :
BaseIO α := do
have : Nonempty α := tasks[0].get
let promise : IO.Promise α IO.Promise.new
tasks.forM <| fun t => BaseIO.chainTask (sync := true) t promise.resolve
return promise.result!.get
namespace Task
/--

View File

@@ -26,7 +26,10 @@ private def Environment.addDeclAux (env : Environment) (opts : Options) (decl :
(cancelTk? : Option IO.CancelToken := none) : Except Kernel.Exception Environment :=
env.addDeclCore (Core.getMaxHeartbeats opts).toUSize decl cancelTk? (!debug.skipKernelTC.get opts)
@[deprecated "use `Lean.addDecl` instead to ensure new namespaces are registered" (since := "2024-12-03")]
def Environment.addDecl (env : Environment) (opts : Options) (decl : Declaration)
(cancelTk? : Option IO.CancelToken := none) : Except Kernel.Exception Environment :=
Environment.addDeclAux env opts decl cancelTk?
private def isNamespaceName : Name Bool
| .str .anonymous _ => true
@@ -47,8 +50,7 @@ where go env
| _ => env
private builtin_initialize privateConstKindsExt : MapDeclarationExtension ConstantKind
-- Use `sync` so we can add entries from anywhere without restrictions
mkMapDeclarationExtension (asyncMode := .sync)
mkMapDeclarationExtension
/--
Returns the kind of the declaration as originally declared instead of as exported. This information
@@ -56,9 +58,7 @@ is stored by `Lean.addDecl` and may be inaccurate if that function was circumven
if the declaration was not found.
-/
def getOriginalConstKind? (env : Environment) (declName : Name) : Option ConstantKind := do
-- Use `local` as for asynchronous decls from the current module, `findAsync?` below will yield
-- the same result but potentially earlier (after `addConstAsync` instead of `addDecl`)
privateConstKindsExt.find? (asyncMode := .local) env declName <|>
privateConstKindsExt.find? env declName <|>
(env.setExporting false |>.findAsync? declName).map (·.kind)
/--

View File

@@ -184,10 +184,10 @@ def registerTagAttribute (name : Name) (descr : String)
let env getEnv
unless (env.getModuleIdxFor? decl).isNone do
throwAttrDeclInImportedModule name decl
unless ext.toEnvExtension.asyncMayModify env decl do
unless env.asyncMayContain decl do
throwAttrNotInAsyncCtx name decl env.asyncPrefix?
validate decl
modifyEnv fun env => ext.addEntry (asyncDecl := decl) env decl
modifyEnv fun env => ext.addEntry env decl
}
registerBuiltinAttribute attrImpl
return { attr := attrImpl, ext := ext }
@@ -199,14 +199,22 @@ def setTag [Monad m] [MonadError m] [MonadEnv m] (attr : TagAttribute) (decl :
let env getEnv
unless (env.getModuleIdxFor? decl).isNone do
throwAttrDeclInImportedModule attr.attr.name decl
unless attr.ext.toEnvExtension.asyncMayModify env decl do
unless env.asyncMayContain decl do
throwAttrNotInAsyncCtx attr.attr.name decl env.asyncPrefix?
modifyEnv fun env => attr.ext.addEntry (asyncDecl := decl) env decl
modifyEnv fun env => attr.ext.addEntry env decl
def hasTag (attr : TagAttribute) (env : Environment) (decl : Name) : Bool :=
match env.getModuleIdxFor? decl with
| some modIdx => (attr.ext.getModuleEntries env modIdx).binSearchContains decl Name.quickLt
| none => (attr.ext.getState (asyncDecl := decl) env).contains decl
| none =>
if attr.ext.toEnvExtension.asyncMode matches .async then
-- It seems that the env extension API doesn't quite allow querying attributes in a way
-- that works for realizable constants, but without waiting on proofs to finish.
-- Until then, we use the following overapproximation, to be refined later:
(attr.ext.findStateAsync env decl).contains decl ||
(attr.ext.getState env (asyncMode := .local)).contains decl
else
(attr.ext.getState env).contains decl
end TagAttribute
@@ -245,7 +253,7 @@ def registerParametricAttribute (impl : ParametricAttributeImpl α) : IO (Parame
unless (env.getModuleIdxFor? decl).isNone do
throwAttrDeclInImportedModule impl.name decl
let val impl.getParam decl stx
modifyEnv fun env => ext.addEntry (asyncDecl := decl) env (decl, val)
modifyEnv fun env => ext.addEntry env (decl, val)
try impl.afterSet decl val catch _ => setEnv env
}
registerBuiltinAttribute attrImpl
@@ -293,9 +301,9 @@ def registerEnumAttributes (attrDescrs : List (Name × String × α))
let r : Array (Name × α) := m.foldl (fun a n p => a.push (n, p)) #[]
r.qsort (fun a b => Name.quickLt a.1 b.1)
statsFn := fun s => "enumeration attribute extension" ++ Format.line ++ "number of local entries: " ++ format s.size
-- We assume (and check in `modifyState`) that, if used asynchronously, enum attributes are set
-- only in the same context in which the tagged declaration was created
asyncMode := .async .mainEnv
-- We assume (and check below) that, if used asynchronously, enum attributes are set only in the
-- same context in which the tagged declaration was created
asyncMode := .async
replay? := some fun _ newState consts st => consts.foldl (init := st) fun st c =>
match newState.find? c with
| some v => st.insert c v
@@ -312,7 +320,7 @@ def registerEnumAttributes (attrDescrs : List (Name × String × α))
unless (env.getModuleIdxFor? decl).isNone do
throwAttrDeclInImportedModule name decl
validate decl val
modifyEnv fun env => ext.addEntry (asyncDecl := decl) env (decl, val)
modifyEnv fun env => ext.addEntry env (decl, val)
applicationTime := applicationTime
: AttributeImpl
}
@@ -327,17 +335,17 @@ def getValue [Inhabited α] (attr : EnumAttributes α) (env : Environment) (decl
match (attr.ext.getModuleEntries env modIdx).binSearch (decl, default) (fun a b => Name.quickLt a.1 b.1) with
| some (_, val) => some val
| none => none
| none => (attr.ext.getState (asyncDecl := decl) env).find? decl
| none => (attr.ext.findStateAsync env decl).find? decl
def setValue (attrs : EnumAttributes α) (env : Environment) (decl : Name) (val : α) : Except String Environment := do
let pfx := s!"Internal error calling `{attrs.ext.name}.setValue` for `{decl}`"
if (env.getModuleIdxFor? decl).isSome then
throw s!"{pfx}: Declaration is in an imported module"
unless attrs.ext.toEnvExtension.asyncMayModify env decl do
if !env.asyncMayContain decl then
throw s!"{pfx}: Declaration is not from this async context `{env.asyncPrefix?}`"
if ((attrs.ext.getState (asyncDecl := decl) env).find? decl).isSome then
if ((attrs.ext.findStateAsync env decl).find? decl).isSome then
throw s!"{pfx}: Attribute has already been set"
return attrs.ext.addEntry (asyncDecl := decl) env (decl, val)
return attrs.ext.addEntry env (decl, val)
end EnumAttributes

View File

@@ -58,7 +58,7 @@ instance : ToString ParamMap := ⟨fun m => Format.pretty (format m)⟩
namespace InitParamMap
/-- Mark parameters that take a reference as borrow -/
def initBorrow (ps : Array Param) : Array Param :=
ps.map fun p => { p with borrow := p.ty.isPossibleRef }
ps.map fun p => { p with borrow := p.ty.isObj }
/-- We do not perform borrow inference for constants marked as `export`.
Reason: we current write wrappers in C++ for using exported functions.

View File

@@ -21,171 +21,155 @@ namespace MaxIndex
our implementation.
-/
structure State where
currentMax : Nat := 0
abbrev Collector := Index Index
abbrev M := StateM State
@[inline] private def skip : Collector := id
@[inline] private def collect (x : Index) : Collector := fun y => if x > y then x else y
@[inline] private def collectVar (x : VarId) : Collector := collect x.idx
@[inline] private def collectJP (j : JoinPointId) : Collector := collect j.idx
@[inline] private def seq (k₁ k₂ : Collector) : Collector := k₂ k₁
instance : AndThen Collector where
andThen a b := private seq a (b ())
private def visitIndex (x : Index) : M Unit := do
modify fun s => { s with currentMax := s.currentMax.max x }
private def collectArg : Arg Collector
| .var x => collectVar x
| .erased => skip
private def visitVar (x : VarId) : M Unit :=
visitIndex x.idx
private def collectArray {α : Type} (as : Array α) (f : α Collector) : Collector :=
fun m => as.foldl (fun m a => f a m) m
private def visitJP (j : JoinPointId) : M Unit :=
visitIndex j.idx
private def collectArgs (as : Array Arg) : Collector := collectArray as collectArg
private def collectParam (p : Param) : Collector := collectVar p.x
private def collectParams (ps : Array Param) : Collector := collectArray ps collectParam
private def visitArg (arg : Arg) : M Unit :=
match arg with
| .var x => visitVar x
| .erased => pure ()
private def visitParam (p : Param) : M Unit :=
visitVar p.x
private def visitExpr (e : Expr) : M Unit := do
match e with
private def collectExpr : Expr Collector
| .proj _ x | .uproj _ x | .sproj _ _ x | .box _ x | .unbox x | .reset _ x | .isShared x =>
visitVar x
collectVar x
| .ctor _ ys | .fap _ ys | .pap _ ys =>
ys.forM visitArg
collectArgs ys
| .ap x ys | .reuse x _ _ ys =>
visitVar x
ys.forM visitArg
| .lit _ => pure ()
collectVar x >> collectArgs ys
| .lit _ => skip
partial def visitFnBody (fnBody : FnBody) : M Unit := do
match fnBody with
private def collectAlts (f : FnBody Collector) (alts : Array Alt) : Collector :=
collectArray alts fun alt => f alt.body
partial def collectFnBody : FnBody Collector
| .vdecl x _ v b =>
visitVar x
visitExpr v
visitFnBody b
collectVar x >> collectExpr v >> collectFnBody b
| .jdecl j ys v b =>
visitJP j
visitFnBody v
ys.forM visitParam
visitFnBody b
collectJP j >> collectFnBody v >> collectParams ys >> collectFnBody b
| .set x _ y b =>
visitVar x
visitArg y
visitFnBody b
collectVar x >> collectArg y >> collectFnBody b
| .uset x _ y b | .sset x _ _ y _ b =>
visitVar x
visitVar y
visitFnBody b
collectVar x >> collectVar y >> collectFnBody b
| .setTag x _ b | .inc x _ _ _ b | .dec x _ _ _ b | .del x b =>
visitVar x
visitFnBody b
collectVar x >> collectFnBody b
| .case _ x _ alts =>
visitVar x
alts.forM (visitFnBody ·.body)
collectVar x >> collectAlts collectFnBody alts
| .jmp j ys =>
visitJP j
ys.forM visitArg
collectJP j >> collectArgs ys
| .ret x =>
visitArg x
| .unreachable => pure ()
collectArg x
| .unreachable => skip
private def visitDecl (decl : Decl) : M Unit := do
match decl with
| .fdecl (xs := xs) (body := b) .. =>
xs.forM visitParam
visitFnBody b
| .extern (xs := xs) .. =>
xs.forM visitParam
partial def collectDecl : Decl Collector
| .fdecl (xs := xs) (body := b) .. => collectParams xs >> collectFnBody b
| .extern (xs := xs) .. => collectParams xs
end MaxIndex
def FnBody.maxIndex (b : FnBody) : Index := Id.run do
let _, { currentMax } := MaxIndex.visitFnBody b |>.run {}
return currentMax
def FnBody.maxIndex (b : FnBody) : Index :=
MaxIndex.collectFnBody b 0
def Decl.maxIndex (d : Decl) : Index := Id.run do
let _, { currentMax } := MaxIndex.visitDecl d |>.run {}
return currentMax
def Decl.maxIndex (d : Decl) : Index :=
MaxIndex.collectDecl d 0
namespace FreeIndices
/-! We say a variable (join point) index (aka name) is free in a function body
if there isn't a `FnBody.vdecl` (`Fnbody.jdecl`) binding it. -/
structure State where
freeIndices : IndexSet := {}
abbrev Collector := IndexSet IndexSet IndexSet
abbrev M := StateM State
@[inline] private def skip : Collector :=
fun _ fv => fv
private def visitIndex (x : Index) : M Unit := do
modify fun s => { s with freeIndices := s.freeIndices.insert x }
@[inline] private def collectIndex (x : Index) : Collector :=
fun bv fv => if bv.contains x then fv else fv.insert x
private def visitVar (x : VarId) : M Unit :=
visitIndex x.idx
@[inline] private def collectVar (x : VarId) : Collector :=
collectIndex x.idx
private def visitJP (j : JoinPointId) : M Unit :=
visitIndex j.idx
@[inline] private def collectJP (x : JoinPointId) : Collector :=
collectIndex x.idx
private def visitArg (arg : Arg) : M Unit :=
match arg with
| .var x => visitVar x
| .erased => pure ()
@[inline] private def withIndex (x : Index) : Collector Collector :=
fun k bv fv => k (bv.insert x) fv
private def visitParam (p : Param) : M Unit :=
visitVar p.x
@[inline] private def withVar (x : VarId) : Collector Collector :=
withIndex x.idx
private def visitExpr (e : Expr) : M Unit := do
match e with
@[inline] private def withJP (x : JoinPointId) : Collector Collector :=
withIndex x.idx
def insertParams (s : IndexSet) (ys : Array Param) : IndexSet :=
ys.foldl (init := s) fun s p => s.insert p.x.idx
@[inline] private def withParams (ys : Array Param) : Collector Collector :=
fun k bv fv => k (insertParams bv ys) fv
@[inline] private def seq : Collector Collector Collector :=
fun k₁ k₂ bv fv => k₂ bv (k₁ bv fv)
instance : AndThen Collector where
andThen a b := private seq a (b ())
private def collectArg : Arg Collector
| .var x => collectVar x
| .erased => skip
private def collectArray {α : Type} (as : Array α) (f : α Collector) : Collector :=
fun bv fv => as.foldl (fun fv a => f a bv fv) fv
private def collectArgs (as : Array Arg) : Collector :=
collectArray as collectArg
private def collectExpr : Expr Collector
| .proj _ x | .uproj _ x | .sproj _ _ x | .box _ x | .unbox x | .reset _ x | .isShared x =>
visitVar x
collectVar x
| .ctor _ ys | .fap _ ys | .pap _ ys =>
ys.forM visitArg
collectArgs ys
| .ap x ys | .reuse x _ _ ys =>
visitVar x
ys.forM visitArg
| .lit _ => pure ()
collectVar x >> collectArgs ys
| .lit _ => skip
partial def visitFnBody (fnBody : FnBody) : M Unit := do
match fnBody with
private def collectAlts (f : FnBody Collector) (alts : Array Alt) : Collector :=
collectArray alts fun alt => f alt.body
partial def collectFnBody : FnBody Collector
| .vdecl x _ v b =>
visitVar x
visitExpr v
visitFnBody b
collectExpr v >> withVar x (collectFnBody b)
| .jdecl j ys v b =>
visitJP j
visitFnBody v
ys.forM visitParam
visitFnBody b
withParams ys (collectFnBody v) >> withJP j (collectFnBody b)
| .set x _ y b =>
visitVar x
visitArg y
visitFnBody b
collectVar x >> collectArg y >> collectFnBody b
| .uset x _ y b | .sset x _ _ y _ b =>
visitVar x
visitVar y
visitFnBody b
collectVar x >> collectVar y >> collectFnBody b
| .setTag x _ b | .inc x _ _ _ b | .dec x _ _ _ b | .del x b =>
visitVar x
visitFnBody b
collectVar x >> collectFnBody b
| .case _ x _ alts =>
visitVar x
alts.forM (visitFnBody ·.body)
collectVar x >>
collectAlts collectFnBody alts
| .jmp j ys =>
visitJP j
ys.forM visitArg
collectJP j >> collectArgs ys
| .ret x =>
visitArg x
| .unreachable => pure ()
private def visitDecl (decl : Decl) : M Unit := do
match decl with
| .fdecl (xs := xs) (body := b) .. =>
xs.forM visitParam
visitFnBody b
| .extern (xs := xs) .. =>
xs.forM visitParam
collectArg x
| .unreachable => skip
end FreeIndices
def FnBody.collectFreeIndices (b : FnBody) (init : IndexSet) : IndexSet := Id.run do
let _, { freeIndices } := FreeIndices.visitFnBody b |>.run { freeIndices := init }
return freeIndices
def FnBody.collectFreeIndices (b : FnBody) (vs : IndexSet) : IndexSet :=
FreeIndices.collectFnBody b {} vs
def FnBody.freeIndices (b : FnBody) : IndexSet :=
b.collectFreeIndices {}

View File

@@ -94,90 +94,77 @@ def mkLiveVarSet (x : VarId) : LiveVarSet :=
namespace LiveVars
structure State where
liveVars : LiveVarSet := {}
abbrev Collector := LiveVarSet LiveVarSet
abbrev M := StateM State
@[inline] private def skip : Collector := fun s => s
@[inline] private def collectVar (x : VarId) : Collector := fun s => s.insert x
private def useVar (x : VarId) : M Unit := do
modify fun s => { s with liveVars := s.liveVars.insert x }
private def collectArg : Arg Collector
| .var x => collectVar x
| .erased => skip
private def bindVar (x : VarId) : M Unit := do
modify fun s => { s with liveVars := s.liveVars.erase x }
private def collectArray {α : Type} (as : Array α) (f : α Collector) : Collector := fun s =>
as.foldl (fun s a => f a s) s
private def useArg (arg : Arg) : M Unit :=
match arg with
| .var x => useVar x
| .erased => pure ()
private def collectArgs (as : Array Arg) : Collector :=
collectArray as collectArg
private def bindParams (ps : Array Param) : M Unit := do
ps.forM (bindVar ·.x)
private def accumulate (s' : LiveVarSet) : Collector :=
fun s => s'.foldl (fun s x => s.insert x) s
private def visitJP (m : JPLiveVarMap) (j : JoinPointId) : M Unit :=
m.get? j |>.forM fun xs =>
modify fun s => { s with liveVars := s.liveVars.merge xs }
private def collectJP (m : JPLiveVarMap) (j : JoinPointId) : Collector :=
match m.get? j with
| some xs => accumulate xs
| none => skip -- unreachable for well-formed code
private def useExpr (e : Expr) : M Unit := do
match e with
private def bindVar (x : VarId) : Collector := fun s =>
s.erase x
private def bindParams (ps : Array Param) : Collector := fun s =>
ps.foldl (fun s p => s.erase p.x) s
def collectExpr : Expr Collector
| .proj _ x | .uproj _ x | .sproj _ _ x | .box _ x | .unbox x | .reset _ x | .isShared x =>
useVar x
collectVar x
| .ctor _ ys | .fap _ ys | .pap _ ys =>
ys.forM useArg
collectArgs ys
| .ap x ys | .reuse x _ _ ys =>
useVar x
ys.forM useArg
| .lit _ => pure ()
collectVar x collectArgs ys
| .lit _ => skip
mutual
private partial def visitFnBody (fnBody : FnBody) (m : JPLiveVarMap) : M Unit := do
partial def collectFnBody (fnBody : FnBody) (m : JPLiveVarMap) : Collector :=
match fnBody with
| .vdecl x _ v b =>
visitFnBody b m
bindVar x
useExpr v
collectExpr v bindVar x collectFnBody b m
| .jdecl j ys v b =>
visitFnBody b <| updateJPLiveVarMap j ys v m
let jLiveVars := (bindParams ys collectFnBody v m) {};
let m := m.insert j jLiveVars;
collectFnBody b m
| .set x _ y b =>
visitFnBody b m
useVar x
useArg y
| .uset x _ y b | .sset x _ _ y _ b =>
visitFnBody b m
useVar x
useVar y
collectVar x collectArg y collectFnBody b m
| .uset x _ y b | .sset x _ _ y _ b =>
collectVar x collectVar y collectFnBody b m
| .setTag x _ b | .inc x _ _ _ b | .dec x _ _ _ b | .del x b =>
visitFnBody b m
useVar x
collectVar x collectFnBody b m
| .case _ x _ alts =>
alts.forM (visitFnBody ·.body m)
useVar x
| .jmp j ys =>
visitJP m j
ys.forM useArg
collectVar x collectArray alts (fun alt => collectFnBody alt.body m)
| .jmp j xs =>
collectJP m j collectArgs xs
| .ret x =>
useArg x
| .unreachable => pure ()
collectArg x
| .unreachable => skip
partial def updateJPLiveVarMap (j : JoinPointId) (ys : Array Param) (v : FnBody) (m : JPLiveVarMap)
: JPLiveVarMap :=
let action : M Unit := do
visitFnBody v m
bindParams ys
let _, { liveVars } := action.run {} |>.run
m.insert j liveVars
end
def updateJPLiveVarMap (j : JoinPointId) (ys : Array Param) (v : FnBody) (m : JPLiveVarMap) : JPLiveVarMap :=
let jLiveVars := (bindParams ys collectFnBody v m) {};
m.insert j jLiveVars
end LiveVars
def updateLiveVars (e : Expr) (v : LiveVarSet) : LiveVarSet :=
let _, { liveVars } := LiveVars.useExpr e |>.run { liveVars := v }
liveVars
LiveVars.collectExpr e v
def collectLiveVars (b : FnBody) (m : JPLiveVarMap) (v : LiveVarSet := {}) : LiveVarSet :=
let _, { liveVars } := LiveVars.visitFnBody b m |>.run { liveVars := v }
liveVars
LiveVars.collectFnBody b m v
export LiveVars (updateJPLiveVarMap)

View File

@@ -20,9 +20,9 @@ that introduce the instructions `release` and `set`
-/
structure VarInfo where
type : IRType
persistent : Bool
inheritsBorrowFromParam : Bool
type : IRType
persistent : Bool -- true if the variable is statically known to be marked a Persistent at runtime
consume : Bool -- true if the variable RC must be "consumed"
deriving Inhabited
abbrev VarMap := Std.TreeMap VarId VarInfo (fun x y => compare x.idx y.idx)
@@ -35,20 +35,28 @@ structure Context where
localCtx : LocalContext := {} -- we use it to store the join point declarations
def getDecl (ctx : Context) (fid : FunId) : Decl :=
findEnvDecl' ctx.env fid ctx.decls |>.get!
match findEnvDecl' ctx.env fid ctx.decls with
| some decl => decl
| none => unreachable!
def getVarInfo (ctx : Context) (x : VarId) : VarInfo :=
ctx.varMap.get! x
match ctx.varMap.get? x with
| some info => info
| none => unreachable!
def getJPParams (ctx : Context) (j : JoinPointId) : Array Param :=
ctx.localCtx.getJPParams j |>.get!
match ctx.localCtx.getJPParams j with
| some ps => ps
| none => unreachable!
def getJPLiveVars (ctx : Context) (j : JoinPointId) : LiveVarSet :=
ctx.jpLiveVarMap.get? j |>.getD {}
match ctx.jpLiveVarMap.get? j with
| some s => s
| none => {}
def mustConsume (ctx : Context) (x : VarId) : Bool :=
let info := getVarInfo ctx x
info.type.isPossibleRef && !info.inheritsBorrowFromParam
info.type.isPossibleRef && info.consume
@[inline] def addInc (ctx : Context) (x : VarId) (b : FnBody) (n := 1) : FnBody :=
let info := getVarInfo ctx x
@@ -107,13 +115,13 @@ private def addIncBeforeAux (ctx : Context) (xs : Array Arg) (consumeParamPred :
let info := getVarInfo ctx x
if !info.type.isPossibleRef || !isFirstOcc xs i then b
else
let numConsumptions := getNumConsumptions x xs consumeParamPred
let numConsuptions := getNumConsumptions x xs consumeParamPred -- number of times the argument is
let numIncs :=
if info.inheritsBorrowFromParam ||
if !info.consume || -- `x` is not a variable that must be consumed by the current procedure
liveVarsAfter.contains x || -- `x` is live after executing instruction
isBorrowParamAux x xs consumeParamPred -- `x` is used in a position that is passed as a borrow reference
then numConsumptions
else numConsumptions - 1
then numConsuptions
else numConsuptions - 1
addInc ctx x b numIncs
private def addIncBefore (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
@@ -121,35 +129,37 @@ private def addIncBefore (ctx : Context) (xs : Array Arg) (ps : Array Param) (b
/-- See `addIncBeforeAux`/`addIncBefore` for the procedure that inserts `inc` operations before an application. -/
private def addDecAfterFullApp (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=
xs.size.fold (init := b) fun i _ b =>
match xs[i] with
| .erased => b
| .var x =>
/- We must add a `dec` if `x` must be consumed, it is alive after the application,
and it has been borrowed by the application.
Remark: `x` may occur multiple times in the application (e.g., `f x y x`).
This is why we check whether it is the first occurrence. -/
if mustConsume ctx x && isFirstOcc xs i && isBorrowParam x xs ps && !bLiveVars.contains x then
addDec ctx x b
else b
xs.size.fold (init := b) fun i _ b =>
match xs[i] with
| .erased => b
| .var x =>
/- We must add a `dec` if `x` must be consumed, it is alive after the application,
and it has been borrowed by the application.
Remark: `x` may occur multiple times in the application (e.g., `f x y x`).
This is why we check whether it is the first occurrence. -/
if mustConsume ctx x && isFirstOcc xs i && isBorrowParam x xs ps && !bLiveVars.contains x then
addDec ctx x b
else b
private def addIncBeforeConsumeAll (ctx : Context) (xs : Array Arg) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
addIncBeforeAux ctx xs (fun _ => true) b liveVarsAfter
/-- Add `dec` instructions for parameters that are references, are not alive in `b`, and are not borrow.
That is, we must make sure these parameters are consumed. -/
private def addDecForDeadParams (ctx : Context) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody × LiveVarSet :=
ps.foldl (init := b, bLiveVars) fun b, bLiveVars p =>
let b :=
if !p.borrow && p.ty.isPossibleRef && !bLiveVars.contains p.x then
addDec ctx p.x b
else b
let bLiveVars := bLiveVars.erase p.x
b, bLiveVars
private def addDecForDeadParams (ctx : Context) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=
ps.foldl (init := b) fun b p =>
if !p.borrow && p.ty.isObj && !bLiveVars.contains p.x then addDec ctx p.x b else b
private def isPersistent : Expr Bool
| .fap _ xs => xs.isEmpty -- all global constants are persistent objects
| _ => false
| Expr.fap _ xs => xs.isEmpty -- all global constants are persistent objects
| _ => false
/-- We do not need to consume the projection of a variable that is not consumed -/
private def consumeExpr (m : VarMap) : Expr Bool
| Expr.proj _ x => match m.get? x with
| some info => info.consume
| none => true
| _ => true
/-- Return true iff `v` at runtime is a scalar value stored in a tagged pointer.
We do not need RC operations for this kind of value. -/
@@ -165,17 +175,11 @@ private def typeForScalarBoxedInTaggedPtr? (v : Expr) : Option IRType :=
| _ => none
private def updateVarInfo (ctx : Context) (x : VarId) (t : IRType) (v : Expr) : Context :=
let inheritsBorrowFromParam :=
match v with
| .proj _ x => match ctx.varMap.get? x with
| some info => info.inheritsBorrowFromParam
| none => false
| _ => false
{ ctx with
varMap := ctx.varMap.insert x {
type := typeForScalarBoxedInTaggedPtr? v |>.getD t
persistent := isPersistent v,
inheritsBorrowFromParam
consume := consumeExpr ctx.varMap v
}
}
@@ -184,105 +188,94 @@ private def addDecIfNeeded (ctx : Context) (x : VarId) (b : FnBody) (bLiveVars :
private def processVDecl (ctx : Context) (z : VarId) (t : IRType) (v : Expr) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody × LiveVarSet :=
let b := match v with
| .ctor _ ys | .reuse _ _ _ ys | .pap _ ys =>
addIncBeforeConsumeAll ctx ys (.vdecl z t v b) bLiveVars
| .proj _ x =>
| (Expr.ctor _ ys) => addIncBeforeConsumeAll ctx ys (FnBody.vdecl z t v b) bLiveVars
| (Expr.reuse _ _ _ ys) => addIncBeforeConsumeAll ctx ys (FnBody.vdecl z t v b) bLiveVars
| (Expr.proj _ x) =>
let b := addDecIfNeeded ctx x b bLiveVars
let b := if !(getVarInfo ctx x).inheritsBorrowFromParam then addInc ctx z b else b
.vdecl z t v b
| .uproj _ x | .sproj _ _ x | .unbox x =>
.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
| .fap f ys =>
let b := if (getVarInfo ctx x).consume then addInc ctx z b else b
(FnBody.vdecl z t v b)
| (Expr.uproj _ x) => FnBody.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
| (Expr.sproj _ _ x) => FnBody.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
| (Expr.fap f ys) =>
let ps := (getDecl ctx f).params
let b := addDecAfterFullApp ctx ys ps b bLiveVars
let b := .vdecl z t v b
let b := FnBody.vdecl z t v b
addIncBefore ctx ys ps b bLiveVars
| .ap x ys =>
| (Expr.pap _ ys) => addIncBeforeConsumeAll ctx ys (FnBody.vdecl z t v b) bLiveVars
| (Expr.ap x ys) =>
let ysx := ys.push (.var x) -- TODO: avoid temporary array allocation
addIncBeforeConsumeAll ctx ysx (.vdecl z t v b) bLiveVars
| .lit _ | .box .. | .reset .. | .isShared _ =>
.vdecl z t v b
addIncBeforeConsumeAll ctx ysx (FnBody.vdecl z t v b) bLiveVars
| (Expr.unbox x) => FnBody.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
| _ => FnBody.vdecl z t v b -- Expr.reset, Expr.box, Expr.lit are handled here
let liveVars := updateLiveVars v bLiveVars
let liveVars := liveVars.erase z
b, liveVars
(b, liveVars)
def updateVarInfoWithParams (ctx : Context) (ps : Array Param) : Context :=
let m := ps.foldl (init := ctx.varMap) fun m p =>
m.insert p.x { type := p.ty, persistent := false, inheritsBorrowFromParam := p.borrow }
m.insert p.x { type := p.ty, persistent := false, consume := !p.borrow }
{ ctx with varMap := m }
partial def visitFnBody (b : FnBody) (ctx : Context) : FnBody × LiveVarSet :=
match b with
| .vdecl x t v b =>
partial def visitFnBody : FnBody Context (FnBody × LiveVarSet)
| FnBody.vdecl x t v b, ctx =>
let ctx := updateVarInfo ctx x t v
let b, bLiveVars := visitFnBody b ctx
let (b, bLiveVars) := visitFnBody b ctx
processVDecl ctx x t v b bLiveVars
| .jdecl j xs v b =>
| FnBody.jdecl j xs v b, ctx =>
let ctxAtV := updateVarInfoWithParams ctx xs
let v, vLiveVars := visitFnBody v ctxAtV
let v, vLiveVars := addDecForDeadParams ctxAtV xs v vLiveVars
let (v, vLiveVars) := visitFnBody v ctxAtV
let v := addDecForDeadParams ctxAtV xs v vLiveVars
let ctx := { ctx with
localCtx := ctx.localCtx.addJP j xs v
jpLiveVarMap := ctx.jpLiveVarMap.insert j vLiveVars
jpLiveVarMap := updateJPLiveVarMap j xs v ctx.jpLiveVarMap
}
let b, bLiveVars := visitFnBody b ctx
.jdecl j xs v b, bLiveVars
| .uset x i y b =>
let b, s := visitFnBody b ctx
let (b, bLiveVars) := visitFnBody b ctx
(FnBody.jdecl j xs v b, bLiveVars)
| FnBody.uset x i y b, ctx =>
let (b, s) := visitFnBody b ctx
-- We don't need to insert `y` since we only need to track live variables that are references at runtime
let s := s.insert x
.uset x i y b, s
| .sset x i o y t b =>
let b, s := visitFnBody b ctx
(FnBody.uset x i y b, s)
| FnBody.sset x i o y t b, ctx =>
let (b, s) := visitFnBody b ctx
-- We don't need to insert `y` since we only need to track live variables that are references at runtime
let s := s.insert x
.sset x i o y t b, s
| .case tid x xType alts =>
let alts : Array (Alt × LiveVarSet) := alts.map fun alt => match alt with
| .ctor c b =>
let ctx := updateRefUsingCtorInfo ctx x c
let b, altLiveVars := visitFnBody b ctx
.ctor c b, altLiveVars
| .default b =>
let b, altLiveVars := visitFnBody b ctx
.default b, altLiveVars
let caseLiveVars : LiveVarSet := alts.foldl (init := {}) fun liveVars _, altLiveVars =>
liveVars.merge altLiveVars
let caseLiveVars := caseLiveVars.insert x
let alts := alts.map fun alt, altLiveVars => match alt with
| .ctor c b =>
let ctx := updateRefUsingCtorInfo ctx x c
let b := addDecForAlt ctx caseLiveVars altLiveVars b
.ctor c b
| .default b =>
let b := addDecForAlt ctx caseLiveVars altLiveVars b
.default b
.case tid x xType alts, caseLiveVars
| .ret x =>
(FnBody.sset x i o y t b, s)
| b@(FnBody.case tid x xType alts), ctx =>
let caseLiveVars := collectLiveVars b ctx.jpLiveVarMap
let alts := alts.map fun alt => match alt with
| Alt.ctor c b =>
let ctx := updateRefUsingCtorInfo ctx x c
let (b, altLiveVars) := visitFnBody b ctx
let b := addDecForAlt ctx caseLiveVars altLiveVars b
Alt.ctor c b
| Alt.default b =>
let (b, altLiveVars) := visitFnBody b ctx
let b := addDecForAlt ctx caseLiveVars altLiveVars b
Alt.default b
(FnBody.case tid x xType alts, caseLiveVars)
| b@(FnBody.ret x), ctx =>
match x with
| .var x =>
let info := getVarInfo ctx x
let b :=
if info.type.isPossibleRef && info.inheritsBorrowFromParam then
addInc ctx x b
else b
b, mkLiveVarSet x
| .erased => b, {}
| .jmp j xs =>
if info.type.isPossibleRef && !info.consume then (addInc ctx x b, mkLiveVarSet x) else (b, mkLiveVarSet x)
| .erased => (b, {})
| b@(FnBody.jmp j xs), ctx =>
let jLiveVars := getJPLiveVars ctx j
let ps := getJPParams ctx j
let b := addIncBefore ctx xs ps b jLiveVars
let bLiveVars := collectLiveVars b ctx.jpLiveVarMap
b, bLiveVars
| .unreachable => .unreachable, {}
| _ => b, {} -- unreachable if well-formed
(b, bLiveVars)
| FnBody.unreachable, _ => (FnBody.unreachable, {})
| other, _ => (other, {}) -- unreachable if well-formed
partial def visitDecl (env : Environment) (decls : Array Decl) (d : Decl) : Decl :=
match d with
| .fdecl (xs := xs) (body := b) .. =>
let ctx := updateVarInfoWithParams { env, decls } xs
let b, bLiveVars := visitFnBody b ctx
let b, _ := addDecForDeadParams ctx xs b bLiveVars
let ctx : Context := { env := env, decls := decls }
let ctx := updateVarInfoWithParams ctx xs
let (b, bLiveVars) := visitFnBody b ctx
let b := addDecForDeadParams ctx xs b bLiveVars
d.updateBody! b
| other => other

View File

@@ -132,6 +132,14 @@ def checkAppArgs (f : Expr) (args : Array Arg) : CheckM Unit := do
let argType arg.inferType
unless ( InferType.compatibleTypes argType expectedType) do
throwError "type mismatch at LCNF application{indentExpr (mkAppN f (args.map Arg.toExpr))}\nargument {arg.toExpr} has type{indentExpr argType}\nbut is expected to have type{indentExpr expectedType}"
unless ( pure (maybeTypeFormerType expectedType) <||> isErasedCompatible expectedType) do
match arg with
| .fvar fvarId => checkFVar fvarId
| .erased => pure ()
| .type _ =>
-- Constructor parameters that are not type formers are erased at phase .mono
unless ( getPhase) .mono && ( isCtorParam f i) do
throwError "invalid LCNF application{indentExpr (mkAppN f (args.map (·.toExpr)))}\nargument{indentExpr arg.toExpr}\nhas type{indentExpr expectedType}\nmust be a free variable"
fType := b
def checkLetValue (e : LetValue) : CheckM Unit := do

View File

@@ -161,20 +161,27 @@ partial def getCtorArgs : Value → Name → Option (Array Value)
partial def ofNat (n : Nat) : Value :=
if n > maxValueDepth then
.top
goBig n n
else
goSmall n
where
goBig (orig : Nat) (curr : Nat) : Value :=
if orig - curr == maxValueDepth then
.top
else
.ctor ``Nat.succ #[goBig orig (curr - 1)]
goSmall : Nat Value
| 0 => .ctor ``Nat.zero #[]
| n + 1 => .ctor ``Nat.succ #[goSmall n]
def ofLCNFLit : LCNF.LitValue Value
| .nat n => ofNat n
-- TODO: Make this work for other numeric literal types.
| .uint8 _ | .uint16 _ | .uint32 _ | .uint64 _ | .usize _ => .top
-- TODO: We could make this much more precise but the payoff is questionable
| .str .. => .top
| .uint8 v => ofNat (UInt8.toNat v)
| .uint16 v => ofNat (UInt16.toNat v)
| .uint32 v => ofNat (UInt32.toNat v)
| .uint64 v | .usize v => ofNat (UInt64.toNat v)
partial def proj : Value Nat Value
| .ctor _ vs , i => vs.getD i bot

View File

@@ -145,7 +145,7 @@ partial def evalApp (declName : Name) (args : Array Arg) : FixParamM Unit := do
have : i < main.params.size := h.2
let param := main.params[i]
let val evalArg args[i]
unless val == .val i || val == .erased do
unless val == .val i || (val == .erased && param.type.isErased) do
-- Found non fixed argument
-- Remark: if the argument is erased and the type of the parameter is erased we assume it is a fixed "propositonal" parameter.
modify fun s => { s with fixed := s.fixed.set! i false }

View File

@@ -150,13 +150,17 @@ partial def inlineApp? (letDecl : LetDecl) (k : Code) : SimpM (Option Code) := d
else
addFVarSubst fvarId result
simp k
markSimplified
if oneExitPointQuick code then
-- TODO: if `k` is small, we should also inline it here
markSimplified
code.bind fun fvarId' => do
markUsedFVar fvarId'
simpK fvarId'
-- else if info.ifReduce then
-- eraseCode code
-- return none
else
markSimplified
let expectedType inferAppType info.fType info.args[*...info.arity]
if expectedType.headBeta.isForall then
/-

View File

@@ -73,7 +73,11 @@ def argsToMonoWithFnType (resultFVar : FVarId) (args : Array Arg) (type : Expr)
def ctorAppToMono (resultFVar : FVarId) (ctorInfo : ConstructorVal) (args : Array Arg)
: ToMonoM LetValue := do
let argsNewParams : Array Arg := .replicate ctorInfo.numParams .erased
let argsNewParams : Array Arg args[*...ctorInfo.numParams].toArray.mapM fun arg => do
-- We only preserve constructor parameters that are types
match arg with
| .type type => return .type ( toMonoType type)
| .fvar .. | .erased => return .erased
let argsNewFields args[ctorInfo.numParams...*].toArray.mapM (argToMonoDeferredCheck resultFVar)
let argsNew := argsNewParams ++ argsNewFields
return .const ctorInfo.name [] argsNew

View File

@@ -173,7 +173,9 @@ where
| _ => return anyExpr
let mut result := fNew
for arg in args do
if isProp arg <||> isPropFormer arg then
if ( isProp arg) then
result := mkApp result erasedExpr
else if ( isPropFormer arg) then
result := mkApp result erasedExpr
else if ( isTypeFormer arg) then
result := mkApp result ( toLCNFType arg)

View File

@@ -12,9 +12,7 @@ public section
namespace Lean
builtin_initialize metaExt : TagDeclarationExtension
-- set by `addPreDefinitions`
mkTagDeclarationExtension (asyncMode := .async .asyncEnv)
builtin_initialize metaExt : TagDeclarationExtension mkTagDeclarationExtension (asyncMode := .async)
/-- Marks in the environment extension that the given declaration has been declared by the user as `meta`. -/
def addMeta (env : Environment) (declName : Name) : Environment :=

View File

@@ -24,15 +24,15 @@ namespace PrefixTreeNode
def empty : PrefixTreeNode α β cmp :=
PrefixTreeNode.Node none
@[inline]
@[specialize]
partial def insert (cmp : α α Ordering) (t : PrefixTreeNode α β cmp) (k : List α) (val : β) : PrefixTreeNode α β cmp :=
let rec @[specialize] insertEmpty (k : List α) : PrefixTreeNode α β cmp :=
let rec insertEmpty (k : List α) : PrefixTreeNode α β cmp :=
match k with
| [] => PrefixTreeNode.Node (some val)
| k :: ks =>
let t := insertEmpty ks
PrefixTreeNode.Node none {(k, t)}
let rec @[specialize] loop
let rec loop
| PrefixTreeNode.Node _ m, [] =>
PrefixTreeNode.Node (some val) m -- overrides old value
| PrefixTreeNode.Node v m, k :: ks =>
@@ -44,7 +44,7 @@ partial def insert (cmp : αα → Ordering) (t : PrefixTreeNode α β cmp)
@[specialize]
partial def find? (cmp : α α Ordering) (t : PrefixTreeNode α β cmp) (k : List α) : Option β :=
let rec @[specialize] loop
let rec loop
| PrefixTreeNode.Node val _, [] => val
| PrefixTreeNode.Node _ m, k :: ks =>
match m.get? k with
@@ -53,9 +53,9 @@ partial def find? (cmp : αα → Ordering) (t : PrefixTreeNode α β cmp)
loop t k
/-- Returns the the value of the longest key in `t` that is a prefix of `k`, if any. -/
@[inline]
@[specialize]
partial def findLongestPrefix? (cmp : α α Ordering) (t : PrefixTreeNode α β cmp) (k : List α) : Option β :=
let rec @[specialize] loop acc?
let rec loop acc?
| PrefixTreeNode.Node val _, [] => val <|> acc?
| PrefixTreeNode.Node val m, k :: ks =>
match m.get? k with
@@ -63,15 +63,15 @@ partial def findLongestPrefix? (cmp : αα → Ordering) (t : PrefixTreeNod
| some t => loop (val <|> acc?) t ks
loop none t k
@[inline]
@[specialize]
partial def foldMatchingM [Monad m] (cmp : α α Ordering) (t : PrefixTreeNode α β cmp) (k : List α) (init : σ) (f : β σ m σ) : m σ :=
let rec @[specialize] fold : PrefixTreeNode α β cmp σ m σ
let rec fold : PrefixTreeNode α β cmp σ m σ
| PrefixTreeNode.Node b? n, d => do
let d match b? with
| none => pure d
| some b => f b d
n.foldlM (init := d) fun d _ t => fold t d
let rec @[specialize] find : List α PrefixTreeNode α β cmp σ m σ
let rec find : List α PrefixTreeNode α β cmp σ m σ
| [], t, d => fold t d
| k::ks, PrefixTreeNode.Node _ m, d =>
match m.get? k with

View File

@@ -28,17 +28,10 @@ def addBuiltinDeclarationRanges (declName : Name) (declRanges : DeclarationRange
builtinDeclRanges.modify (·.insert declName declRanges)
def addDeclarationRanges [Monad m] [MonadEnv m] (declName : Name) (declRanges : DeclarationRanges) : m Unit := do
if declName.isAnonymous then
-- This can happen on elaboration of partial syntax and would panic in `modifyState` otherwise
return
modifyEnv fun env => declRangeExt.insert env declName declRanges
def findDeclarationRangesCore? [Monad m] [MonadEnv m] (declName : Name) : m (Option DeclarationRanges) :=
-- In the case of private definitions imported via `import all`, looking in `.olean.server` is not
-- sufficient, so we look in the actual environment as well via `exported` (TODO: rethink
-- parameter naming).
return declRangeExt.find? (level := .exported) ( getEnv) declName <|>
declRangeExt.find? (level := .server) ( getEnv) declName
return declRangeExt.find? (level := .server) ( getEnv) declName
def findDeclarationRanges? [Monad m] [MonadEnv m] [MonadLiftT BaseIO m] (declName : Name) : m (Option DeclarationRanges) := do
let env getEnv

View File

@@ -68,7 +68,7 @@ need to be unfolded to prove the theorem are exported and exposed.
builtin_initialize defeqAttr : TagAttribute
registerTagAttribute `defeq "mark theorem as a definitional equality, to be used by `dsimp`"
(validate := validateDefEqAttr) (applicationTime := .afterTypeChecking)
(asyncMode := .async .mainEnv)
(asyncMode := .async)
private partial def isRflProofCore (type : Expr) (proof : Expr) : CoreM Bool := do
match type with

View File

@@ -67,6 +67,17 @@ structure BinderView where
type : Syntax
bi : BinderInfo
/--
Determines the local declaration kind depending on the variable name.
The `__x` in `let __x := 42; body` gets kind `.implDetail`.
-/
def kindOfBinderName (binderName : Name) : LocalDeclKind :=
if binderName.isImplementationDetail then
.implDetail
else
.default
partial def quoteAutoTactic : Syntax CoreM Expr
| .ident _ _ val preresolved =>
return mkApp4 (.const ``Syntax.ident [])
@@ -224,7 +235,8 @@ private partial def elabBinderViews (binderViews : Array BinderView) (fvars : Ar
throwErrorAt binderView.type (m!"invalid binder annotation, type is not a class instance{indentExpr type}" ++ .note "Use the command `set_option checkBinderAnnotations false` to disable the check")
withRef binderView.type <| checkLocalInstanceParameters type
let id := binderView.id.getId
withLocalDecl id binderView.bi type (kind := .ofBinderName id) fun fvar => do
let kind := kindOfBinderName id
withLocalDecl id binderView.bi type (kind := kind) fun fvar => do
addLocalVarInfo binderView.ref fvar
loop (i+1) (fvars.push (binderView.id, fvar))
else
@@ -433,7 +445,7 @@ private partial def elabFunBinderViews (binderViews : Array BinderView) (i : Nat
let fvar := mkFVar fvarId
let s := { s with fvars := s.fvars.push fvar }
let id := binderView.id.getId
let kind := .ofBinderName id
let kind := kindOfBinderName id
/-
We do **not** want to support default and auto arguments in lambda abstractions.
Example: `fun (x : Nat := 10) => x+1`.
@@ -796,7 +808,7 @@ def elabLetDeclAux (id : Syntax) (binders : Array Syntax) (typeStx : Syntax) (va
-/
let val mkLambdaFVars fvars val (usedLetOnly := false)
pure (type, val, binders)
let kind := .ofBinderName id.getId
let kind := kindOfBinderName id.getId
trace[Elab.let.decl] "{id.getId} : {type} := {val}"
let result
withLetDecl id.getId (kind := kind) type val (nondep := config.nondep) fun x => do

View File

@@ -11,17 +11,6 @@ meta import Lean.Parser.Term
public section
/--
Determines the local declaration kind of a binder using its name.
Names that begin with `__` are implementation details (`.implDetail`).
-/
def Lean.LocalDeclKind.ofBinderName (binderName : Name) : LocalDeclKind :=
if binderName.isImplementationDetail then
.implDetail
else
.default
namespace Lean.Elab.Term
/--
Recall that

View File

@@ -21,11 +21,8 @@ Ensure the environment does not contain a declaration with name `declName`.
Recall that a private declaration cannot shadow a non-private one and vice-versa, although
they internally have different names.
-/
def checkNotAlreadyDeclared {m} [Monad m] [MonadEnv m] [MonadError m] [MonadFinally m] [MonadInfoTree m] (declName : Name) : m Unit := do
def checkNotAlreadyDeclared {m} [Monad m] [MonadEnv m] [MonadError m] [MonadInfoTree m] (declName : Name) : m Unit := do
let env getEnv
-- Even if `declName` is public, in this function we want to consider private declarations as well
let env := env.setExporting false
withEnv env do -- also set as context for any exceptions
let addInfo declName := do
pushInfoLeaf <| .ofTermInfo {
elaborator := .anonymous, lctx := {}, expectedType? := none
@@ -169,7 +166,7 @@ def expandOptDocComment? [Monad m] [MonadError m] (optDocComment : Syntax) : m (
section Methods
variable [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadFinally m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadInfoTree m] [MonadLiftT IO m]
variable [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadInfoTree m] [MonadLiftT IO m]
/-- Elaborate declaration modifiers (i.e., attributes, `partial`, `private`, `protected`, `unsafe`, `meta`, `noncomputable`, doc string)-/
def elabModifiers (stx : TSyntax ``Parser.Command.declModifiers) : m Modifiers := do

View File

@@ -68,12 +68,11 @@ def mkAuxFunction (ctx : Context) (i : Nat) : TermElabM Command := do
let letDecls mkLocalInstanceLetDecls ctx `Hashable header.argNames
body mkLet letDecls body
let binders := header.binders
let vis := ctx.mkVisibilityFromTypes
if ctx.usePartial then
-- TODO(Dany): Get rid of this code branch altogether once we have well-founded recursion
`($vis:visibility partial def $(mkIdent auxFunName):ident $binders:bracketedBinder* : UInt64 := $body:term)
`(partial def $(mkIdent auxFunName):ident $binders:bracketedBinder* : UInt64 := $body:term)
else
`(@[no_expose] $vis:visibility def $(mkIdent auxFunName):ident $binders:bracketedBinder* : UInt64 := $body:term)
`(def $(mkIdent auxFunName):ident $binders:bracketedBinder* : UInt64 := $body:term)
def mkHashFuncs (ctx : Context) : TermElabM Syntax := do
let mut auxDefs := #[]
@@ -88,7 +87,6 @@ private def mkHashableInstanceCmds (declName : Name) : TermElabM (Array Syntax)
return cmds
def mkHashableHandler (declNames : Array Name) : CommandElabM Bool := do
withoutExporting do -- This deriving handler handles visibility of generated decls syntactically
if ( declNames.allM isInductive) then
for declName in declNames do
let cmds liftTermElabM <| mkHashableInstanceCmds declName

View File

@@ -56,6 +56,11 @@ def processHeaderCore
else
.private
let (env, messages) try
for i in imports do
if !isModule && i.importAll then
throw <| .userError "cannot use `import all` without `module`"
if i.importAll && mainModule.getRoot != i.module.getRoot then
throw <| .userError "cannot use `import all` across module path roots"
let env
importModules (leakEnv := leakEnv) (loadExts := true) (level := level)
imports opts trustLevel plugins arts

View File

@@ -115,15 +115,12 @@ private def registerLetRecsToLift (views : Array LetRecDeclView) (fvars : Array
let toLift views.mapIdxM fun i view => do
let value := values[i]!
let termination := view.termination.rememberExtraParams view.binderIds.size value
let env getEnv
pure {
ref := view.ref
fvarId := fvars[i]!.fvarId!
attrs := view.attrs
shortDeclName := view.shortDeclName
declName :=
if env.isExporting || !env.header.isModule then view.declName
else mkPrivateName env view.declName
declName := view.declName
parentName? := view.parentName?
lctx
localInstances

View File

@@ -199,8 +199,7 @@ private partial def withPatternVars {α} (pVars : Array PatternVar) (k : Array P
let rec loop (i : Nat) (decls : Array PatternVarDecl) (userNames : Array Name) := do
if h : i < pVars.size then
let type mkFreshTypeMVar
let n := pVars[i].getId
withLocalDecl n BinderInfo.default type (kind := .ofBinderName n) fun x =>
withLocalDecl pVars[i].getId BinderInfo.default type fun x =>
loop (i+1) (decls.push { fvarId := x.fvarId! }) (userNames.push Name.anonymous)
else
k decls
@@ -745,7 +744,7 @@ where
let rec go (packed : Expr) (patternVars : Array Expr) : TermElabM α := do
match packed with
| .lam n d b _ =>
withLocalDecl n .default ( erasePatternRefAnnotations ( eraseInaccessibleAnnotations d)) (kind := .ofBinderName n) fun patternVar =>
withLocalDeclD n ( erasePatternRefAnnotations ( eraseInaccessibleAnnotations d)) fun patternVar =>
go (b.instantiate1 patternVar) (patternVars.push patternVar)
| _ =>
let (matchType, patterns) := unpackMatchTypePatterns packed

View File

@@ -1236,8 +1236,13 @@ where
let ctx CommandContextInfo.save
infoPromise.resolve <| .context (.commandCtx ctx) <| .node info ( getInfoTrees)
async.commitConst ( getEnv)
processDeriving #[header]
async.commitCheckEnv ( getEnv)
let cancelTk IO.CancelToken.new
let checkAct wrapAsyncAsSnapshot (desc := s!"finishing proof of {declId.declName}")
(cancelTk? := cancelTk) fun _ => do profileitM Exception "elaboration" ( getOptions) do
processDeriving #[header]
async.commitCheckEnv ( getEnv)
let checkTask BaseIO.mapTask (t := ( getEnv).checked) checkAct
Core.logSnapshotTask { stx? := none, task := checkTask, cancelTk? := cancelTk }
Core.logSnapshotTask { stx? := none, task := ( BaseIO.asTask (act ())), cancelTk? := cancelTk }
applyAttributesAt declId.declName view.modifiers.attrs .afterTypeChecking
applyAttributesAt declId.declName view.modifiers.attrs .afterCompilation

View File

@@ -1,7 +1,7 @@
/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Sebastian Ullrich, Mac Malone
Authors: Leonardo de Moura
-/
module
@@ -16,7 +16,6 @@ namespace ParseImports
structure State where
imports : Array Import := #[]
pos : String.Pos := 0
badModifier : Bool := false
error? : Option String := none
isModule : Bool := false
-- per-import fields to be consumed by `moduleIdent`
@@ -27,9 +26,8 @@ structure State where
@[expose] def Parser := String State State
@[inline] def skip : Parser := fun _ s => s
instance : Inhabited Parser := skip
instance : Inhabited Parser where
default := fun _ s => s
@[inline] def State.setPos (s : State) (pos : String.Pos) : State :=
{ s with pos := pos }
@@ -40,9 +38,6 @@ instance : Inhabited Parser := ⟨skip⟩
def State.mkEOIError (s : State) : State :=
s.mkError "unexpected end of input"
@[inline] def State.clearError (s : State) : State :=
{ s with error? := none, badModifier := false }
@[inline] def State.next (s : State) (input : String) (pos : String.Pos) : State :=
{ s with pos := input.next pos }
@@ -130,8 +125,8 @@ partial def whitespace : Parser := fun input s =>
go (k.next' i h₁) (input.next' j h₂)
go 0 s.pos
@[inline] def keyword (k : String) : Parser :=
keywordCore k (fun _ s => s.mkError s!"`{k}` expected") skip
@[inline] partial def keyword (k : String) : Parser :=
keywordCore k (fun _ s => s.mkError s!"`{k}` expected") (fun _ s => s)
@[inline] def isIdCont : String State Bool := fun input s =>
let i := s.pos
@@ -157,9 +152,7 @@ def State.pushImport (i : Import) (s : State) : State :=
partial def moduleIdent : Parser := fun input s =>
let finalize (module : Name) : Parser := fun input s =>
let imp := { module, isMeta := s.isMeta, importAll := s.importAll, isExported := s.isExported }
let s := whitespace input (s.pushImport imp)
{s with isMeta := false, importAll := false, isExported := !s.isModule}
whitespace input (s.pushImport { module, isMeta := s.isMeta, importAll := s.importAll, isExported := s.isExported })
let rec parse (module : Name) (s : State) :=
let i := s.pos
if h : input.atEnd i then
@@ -194,50 +187,30 @@ partial def moduleIdent : Parser := fun input s =>
s.mkError "expected identifier"
parse .anonymous s
@[inline] def atomic (p : Parser) : Parser := fun input s =>
@[specialize] partial def many (p : Parser) : Parser := fun input s =>
let pos := s.pos
let size := s.imports.size
let s := p input s
if s.error? matches some .. then {s with pos} else s
match s.error? with
| none => many p input s
| some _ => { s with pos, error? := none, imports := s.imports.shrink size }
@[specialize] partial def manyImports (p : Parser) : Parser := fun input s =>
let pos := s.pos
let s := p input s
if s.error? matches some .. then
if s.pos == pos then s.clearError else s
else if s.badModifier then
let err := "cannot use 'public', 'meta', or 'all' without 'module'"
{s with pos, badModifier := false, error? := some err}
else
manyImports p input s
def setIsMeta (isMeta : Bool) : Parser := fun _ s =>
{ s with isMeta }
def setIsModule (isModule : Bool) : Parser := fun _ s =>
{ s with isModule, isExported := !isModule }
def setIsExported (isExported : Bool) : Parser := fun _ s =>
{ s with isExported := isExported || !s.isModule }
def setMeta : Parser := fun _ s =>
if s.isModule then
{ s with isMeta := true }
else
{ s with badModifier := true }
def setExported : Parser := fun _ s =>
if s.isModule then
{ s with isExported := true }
else
{ s with badModifier := true }
def setImportAll : Parser := fun _ s =>
if s.isModule then
{ s with importAll := true }
else
{ s with badModifier := true }
def setImportAll (importAll : Bool) : Parser := fun _ s =>
{ s with importAll }
def main : Parser :=
keywordCore "module" (setIsModule false) (setIsModule true) >>
keywordCore "prelude" (fun _ s => s.pushImport `Init) skip >>
manyImports (atomic (keywordCore "public" skip setExported >>
keywordCore "meta" skip setMeta >>
keyword "import") >>
keywordCore "all" skip setImportAll >>
keywordCore "module" (fun _ s => s) (fun _ s => { s with isModule := true }) >>
keywordCore "prelude" (fun _ s => s.pushImport `Init) (fun _ s => s) >>
many (keywordCore "public" (setIsExported false) (setIsExported true) >>
keywordCore "meta" (setIsMeta false) (setIsMeta true) >>
keyword "import" >>
keywordCore "all" (setImportAll false) (setImportAll true) >>
moduleIdent)
end ParseImports
@@ -247,11 +220,9 @@ Simpler and faster version of `parseImports`. We use it to implement Lake.
-/
def parseImports' (input : String) (fileName : String) : IO ModuleHeader := do
let s := ParseImports.main input (ParseImports.whitespace input {})
let some err := s.error?
| return { s with }
let fileMap := input.toFileMap
let pos := fileMap.toPosition s.pos
throw <| .userError s!"{fileName}:{pos.line}:{pos.column}: {err}"
match s.error? with
| none => return { s with }
| some err => throw <| IO.userError s!"{fileName}: {err}"
structure PrintImportResult where
result? : Option ModuleHeader := none

View File

@@ -141,29 +141,28 @@ private def betaReduceLetRecApps (preDefs : Array PreDefinition) : MetaM (Array
private def addSorried (preDefs : Array PreDefinition) : TermElabM Unit := do
for preDef in preDefs do
unless ( hasConst preDef.declName) do
let value mkSorry (synthetic := true) preDef.type
let decl := if preDef.kind.isTheorem then
Declaration.thmDecl {
name := preDef.declName,
levelParams := preDef.levelParams,
type := preDef.type,
value
}
else
Declaration.defnDecl {
name := preDef.declName,
levelParams := preDef.levelParams,
type := preDef.type,
hints := .abbrev
safety := .safe
value
}
addDecl decl
withSaveInfoContext do -- save new env
addTermInfo' preDef.ref ( mkConstWithLevelParams preDef.declName) (isBinder := true)
applyAttributesOf #[preDef] AttributeApplicationTime.afterTypeChecking
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
let value mkSorry (synthetic := true) preDef.type
let decl := if preDef.kind.isTheorem then
Declaration.thmDecl {
name := preDef.declName,
levelParams := preDef.levelParams,
type := preDef.type,
value
}
else
Declaration.defnDecl {
name := preDef.declName,
levelParams := preDef.levelParams,
type := preDef.type,
hints := .abbrev
safety := .safe
value
}
addDecl decl
withSaveInfoContext do -- save new env
addTermInfo' preDef.ref ( mkConstWithLevelParams preDef.declName) (isBinder := true)
applyAttributesOf #[preDef] AttributeApplicationTime.afterTypeChecking
applyAttributesOf #[preDef] AttributeApplicationTime.afterCompilation
def ensureFunIndReservedNamesAvailable (preDefs : Array PreDefinition) : MetaM Unit := do
preDefs.forM fun preDef =>

View File

@@ -11,6 +11,7 @@ public import Lean.Meta.Tactic.Split
public import Lean.Elab.PreDefinition.Basic
public import Lean.Elab.PreDefinition.Eqns
public import Lean.Meta.ArgsPacker.Basic
public import Lean.Elab.PreDefinition.WF.Unfold
public import Lean.Elab.PreDefinition.FixedParams
public import Init.Data.Array.Basic
@@ -67,7 +68,7 @@ def copyPrivateUnfoldTheorem : GetUnfoldEqnFn := fun declName => do
withTraceNode `ReservedNameAction (pure m!"{exceptOptionEmoji ·} copyPrivateUnfoldTheorem running for {declName}") do
let name := mkEqLikeNameFor ( getEnv) declName unfoldThmSuffix
if let some mod findModuleOf? declName then
let unfoldName' := mkPrivateNameCore mod (.str (privateToUserName declName) unfoldThmSuffix)
let unfoldName' := mkPrivateNameCore mod (.str declName unfoldThmSuffix)
if let some (.thmInfo info) := ( getEnv).find? unfoldName' then
realizeConst declName name do
addDecl <| Declaration.thmDecl {

View File

@@ -1,34 +1,23 @@
/-
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Leonardo de Moura, Joachim Breitner
Authors: Leonardo de Moura
-/
module
prelude
public import Lean.Elab.PreDefinition.Basic
import Lean.Elab.PreDefinition.Eqns
import Lean.Meta.Tactic.Apply
import Lean.Meta.Tactic.Split
public import Lean.Meta.Tactic.Simp.Types
public import Lean.Elab.PreDefinition.Eqns
public import Lean.Meta.Tactic.Apply
import Lean.Meta.Tactic.Simp.Main
import Lean.Meta.Tactic.Simp.BuiltinSimprocs
/-!
This module is responsible for proving the unfolding equation for functions defined
by well-founded recursion. It uses `WellFounded.fix_eq`, and then has to undo
the changes to matchers that `WF.Fix` did using `MatcherApp.addArg`.
This is done using a single-pass `simp` traversal of the expression that looks
for expressions that were modified that way, and rewrites them back using the
rather specialized `_arg_pusher` theorem that is generated by `mkMatchArgPusher`.
-/
public section
namespace Lean.Elab.WF
open Meta
open Eqns
def rwFixEq (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
private def rwFixEq (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
let target mvarId.getType'
let some (_, lhs, rhs) := target.eq? | unreachable!
@@ -54,157 +43,44 @@ def rwFixEq (mvarId : MVarId) : MetaM MVarId := mvarId.withContext do
mvarId.assign ( mkEqTrans h mvarNew)
return mvarNew.mvarId!
def isForallMotive (matcherApp : MatcherApp) : MetaM (Option Expr) := do
lambdaBoundedTelescope matcherApp.motive matcherApp.discrs.size fun xs t =>
if xs.size == matcherApp.discrs.size && t.isForall && !t.bindingBody!.hasLooseBVar 0 then
return some ( mkLambdaFVars xs t.bindingBody!)
else
return none
/-- Generalization of `splitMatch` that can handle `casesOn` -/
def splitMatchOrCasesOn (mvarId : MVarId) (e : Expr) (matcherInfo : MatcherInfo) : MetaM (List MVarId) := do
if ( isMatcherApp e) then
Split.splitMatch mvarId e
private partial def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := do
trace[Elab.definition.wf.eqns] "step\n{MessageData.ofGoal mvarId}"
if withAtLeastTransparency .all (tryURefl mvarId) then
trace[Elab.definition.wf.eqns] "refl!"
return ()
else if ( tryContradiction mvarId) then
trace[Elab.definition.wf.eqns] "contradiction!"
return ()
else if let some mvarId simpMatch? mvarId then
trace[Elab.definition.wf.eqns] "simpMatch!"
mkUnfoldProof declName mvarId
else if let some mvarId simpIf? mvarId (useNewSemantics := true) then
trace[Elab.definition.wf.eqns] "simpIf!"
mkUnfoldProof declName mvarId
else
assert! matcherInfo.numDiscrs = 1
let discr := e.getAppArgs[matcherInfo.numParams + 1]!
assert! discr.isFVar
let subgoals mvarId.cases discr.fvarId!
return subgoals.map (·.mvarId) |>.toList
let ctx Simp.mkContext (config := { dsimp := false, etaStruct := .none })
match ( simpTargetStar mvarId ctx (simprocs := {})).1 with
| TacticResultCNM.closed => return ()
| TacticResultCNM.modified mvarId =>
trace[Elab.definition.wf.eqns] "simp only!"
mkUnfoldProof declName mvarId
| TacticResultCNM.noChange =>
if let some mvarIds casesOnStuckLHS? mvarId then
trace[Elab.definition.wf.eqns] "case split into {mvarIds.size} goals"
mvarIds.forM (mkUnfoldProof declName)
else if let some mvarIds splitTarget? mvarId (useNewSemantics := true) then
trace[Elab.definition.wf.eqns] "splitTarget into {mvarIds.length} goals"
mvarIds.forM (mkUnfoldProof declName)
else
-- At some point in the past, we looked for occurrences of Wf.fix to fold on the
-- LHS (introduced in 096e4eb), but it seems that code path was never used,
-- so #3133 removed it again (and can be recovered from there if this was premature).
throwError "failed to generate equational theorem for '{declName}'\n{MessageData.ofGoal mvarId}"
/--
Generates a theorem of the form
```
matcherArgPusher params motive {α} {β} (f : ∀ (x : α), β x) rel alt1 .. x1 x2
:
matcher params (motive := fun x1 x2 => ((y : α) → rel x1 x2 y → β y) → motive x1 x2)
(alt1 := fun z1 z2 z2 f => alt1 z1 z2 z2 f) …
x1 x2
(fun y _h => f y)
=
matcher params (motive := motive)
(alt1 := fun z1 z2 z2 => alt1 z1 z2 z2 (fun y _ => f y)) …
x1 x2
```
-/
def mkMatchArgPusher (matcherName : Name) (matcherInfo : MatcherInfo) : MetaM Name := do
let name := (mkPrivateName ( getEnv) matcherName) ++ `_arg_pusher
realizeConst matcherName name do
let matcherVal getConstVal matcherName
forallBoundedTelescope matcherVal.type (some (matcherInfo.numParams + 1)) fun xs _ => do
let params := xs[*...matcherInfo.numParams]
let motive' := xs[matcherInfo.numParams]!
let u mkFreshUserName `u
let v mkFreshUserName `v
withLocalDeclD `α (.sort (.param u)) fun alpha => do
withLocalDeclD ( mkArrow alpha (.sort (.param v))) fun beta => do
withLocalDeclD `f (.forallE `x alpha (mkApp beta (.bvar 0)) .default) fun f => do
let relType forallTelescope ( inferType motive') fun xs _ =>
mkForallFVars xs (.forallE `x alpha (.sort 0) .default)
withLocalDeclD `rel relType fun rel => do
let motive forallTelescope ( inferType motive') fun xs _ => do
let motiveBody := mkAppN motive' xs
let extraArgType := .forallE `y alpha (.forallE `h (mkAppN rel (xs.push (.bvar 0))) (mkApp beta (.bvar 1)) .default) .default
let motiveBody mkArrow extraArgType motiveBody
mkLambdaFVars xs motiveBody
let uElim lambdaBoundedTelescope motive matcherInfo.numDiscrs fun _ motiveBody => do
getLevel motiveBody
let us := matcherVal.levelParams ++ [u, v]
let matcherLevels' := matcherVal.levelParams.map mkLevelParam
let matcherLevels match matcherInfo.uElimPos? with
| none =>
unless uElim.isZero do
throwError "unexpected matcher application for {.ofConstName matcherName}, motive is not a proposition"
pure matcherLevels'
| some pos =>
pure <| (matcherLevels'.toArray.set! pos uElim).toList
let lhs := .const matcherName matcherLevels
let rhs := .const matcherName matcherLevels'
let lhs := mkAppN lhs params
let rhs := mkAppN rhs params
let lhs := mkApp lhs motive
let rhs := mkApp rhs motive'
forallBoundedTelescope ( inferType lhs) matcherInfo.numDiscrs fun discrs _ => do
let lhs := mkAppN lhs discrs
let rhs := mkAppN rhs discrs
forallBoundedTelescope ( inferType lhs) matcherInfo.numAlts fun alts _ => do
let lhs := mkAppN lhs alts
let mut rhs := rhs
for alt in alts, altNumParams in matcherInfo.altNumParams do
let alt' forallBoundedTelescope ( inferType alt) altNumParams fun ys altBodyType => do
assert! altBodyType.isForall
let altArg forallBoundedTelescope altBodyType.bindingDomain! (some 2) fun ys _ => do
mkLambdaFVars ys (.app f ys[0]!)
mkLambdaFVars ys (mkAppN alt (ys.push altArg))
rhs := mkApp rhs alt'
let extraArg := .lam `y alpha (.lam `h (mkAppN rel (discrs.push (.bvar 0))) (mkApp f (.bvar 1)) .default) .default
let lhs := mkApp lhs extraArg
let goal mkEq lhs rhs
let value mkFreshExprSyntheticOpaqueMVar goal
let mvarId := value.mvarId!
let mvarIds splitMatchOrCasesOn mvarId rhs matcherInfo
for mvarId in mvarIds do
mvarId.refl
let value instantiateMVars value
let type mkForallFVars (params ++ #[motive', alpha, beta, f, rel] ++ discrs ++ alts) goal
let value mkLambdaFVars (params ++ #[motive', alpha, beta, f, rel] ++ discrs ++ alts) value
addDecl <| Declaration.thmDecl { name, levelParams := us, type, value}
return name
builtin_simproc_decl matcherPushArg (_) := fun e => do
let e := e.headBeta
let some matcherApp matchMatcherApp? e (alsoCasesOn := true) | return .continue
-- Check that the first remaining argument is of the form `(fun (x : α) p => (f x : β x))`
let some fArg := matcherApp.remaining[0]? | return .continue
unless fArg.isLambda do return .continue
unless fArg.bindingBody!.isLambda do return .continue
unless fArg.bindingBody!.bindingBody!.isApp do return .continue
if fArg.bindingBody!.bindingBody!.hasLooseBVar 0 then return .continue
unless fArg.bindingBody!.bindingBody!.appArg! == .bvar 1 do return .continue
if fArg.bindingBody!.bindingBody!.appFn!.hasLooseBVar 1 then return .continue
let fExpr := fArg.bindingBody!.bindingBody!.appFn!
let fExprType inferType fExpr
let fExprType withTransparency .all (whnfForall fExprType)
assert! fExprType.isForall
let alpha := fExprType.bindingDomain!
let beta := .lam fExprType.bindingName! fExprType.bindingDomain! fExprType.bindingBody! .default
-- Check that the motive has an extra parameter (from MatcherApp.addArg)
let some motive' isForallMotive matcherApp | return .continue
let rel lambdaTelescope matcherApp.motive fun xs motiveBody =>
let motiveBodyArg := motiveBody.bindingDomain!
mkLambdaFVars xs (.lam motiveBodyArg.bindingName! motiveBodyArg.bindingDomain! motiveBodyArg.bindingBody!.bindingDomain! .default)
let argPusher mkMatchArgPusher matcherApp.matcherName matcherApp.toMatcherInfo
-- Let's infer the level paramters:
let proof withTransparency .all <| mkAppOptM
argPusher ((matcherApp.params ++ #[motive', alpha, beta, fExpr, rel] ++ matcherApp.discrs ++ matcherApp.alts).map some)
let some (_, _, rhs) := ( inferType proof).eq? | throwError "matcherPushArg: expected equality:{indentExpr (← inferType proof)}"
let step : Simp.Result := { expr := rhs, proof? := some proof }
let step step.addExtraArgs matcherApp.remaining[1...*]
return .continue (some step)
def mkUnfoldProof (declName : Name) (mvarId : MVarId) : MetaM Unit := withTransparency .all do
let ctx Simp.mkContext (config := { dsimp := false, etaStruct := .none, letToHave := false, singlePass := true })
let simprocs := ({} : Simp.SimprocsArray)
let simprocs simprocs.add ``matcherPushArg (post := false)
match ( simpTarget mvarId ctx (simprocs := simprocs)).1 with
| none => return ()
| some mvarId' =>
prependError m!"failed to finish proof for equational theorem for '{.ofConstName declName}'" do
mvarId'.refl
public def mkUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) (wfPreprocessProof : Simp.Result) : MetaM Unit := do
def mkUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) (wfPreprocessProof : Simp.Result) : MetaM Unit := do
let name := mkEqLikeNameFor ( getEnv) preDef.declName unfoldThmSuffix
prependError m!"Cannot derive unfold equation {name}" do
prependError m!"Cannot derive {name}" do
withOptions (tactic.hygienic.set · false) do
withoutExporting do
lambdaTelescope preDef.value fun xs body => do
let us := preDef.levelParams.map mkLevelParam
let lhs := mkAppN (Lean.mkConst preDef.declName us) xs
@@ -235,7 +111,7 @@ theorem of `foo._unary` or `foo._binary`.
It should just be a specialization of that one, due to defeq.
-/
public def mkBinaryUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) : MetaM Unit := do
def mkBinaryUnfoldEq (preDef : PreDefinition) (unaryPreDefName : Name) : MetaM Unit := do
let name := mkEqLikeNameFor ( getEnv) preDef.declName unfoldThmSuffix
let unaryEqName:= mkEqLikeNameFor ( getEnv) unaryPreDefName unfoldThmSuffix
prependError m!"Cannot derive {name} from {unaryEqName}" do

View File

@@ -34,10 +34,6 @@ private def mkHeader (kind : String) (id : Name) (levelParams : List Name) (type
| ReducibilityStatus.reducible => attrs := attrs.push m!"reducible"
| ReducibilityStatus.semireducible => pure ()
let env getEnv
if env.header.isModule && (env.setExporting true |>.find? id |>.any (·.isDefinition)) then
attrs := attrs.push m!"expose"
if defeqAttr.hasTag ( getEnv) id then
attrs := attrs.push m!"defeq"

View File

@@ -98,7 +98,7 @@ partial def countUsesDecl (fvarId : FVarId) (ty : Expr) (val? : Option Expr) (bo
partial def countUses (e : Expr) (subst : Array FVarId := #[]) : MetaM (Expr × FVarUses) := match e with
| .bvar n =>
if _ : n < subst.size then
return (e, {(subst[subst.size - 1 - n], .one)})
return (e, {(subst[n], .one)})
else
throwError "BVar index out of bounds: {n} >= {subst.size}"
| .fvar fvarId => return (e, {(fvarId, .one)})
@@ -158,7 +158,7 @@ def elimLetsCore (e : Expr) (elimTrivial := true) : MetaM Expr := StateRefT'.run
| _ => return .continue
Meta.transform e (pre := pre)
def elimLets (mvar : MVarId) (elimTrivial := true) : MetaM MVarId := mvar.withContext do
def elimLets (mvar : MVarId) (elimTrivial := true): MetaM MVarId := mvar.withContext do
let ctx getLCtx
let (ty, fvarUses) countUses ( mvar.getType)
let ctx countUsesLCtx ctx fvarUses

View File

@@ -26,7 +26,7 @@ def mStart (goal : Expr) : MetaM MStartResult := do
return { goal := mgoal }
let u mkFreshLevelMVar
let σs mkFreshExprMVar (TypeList.mkType u)
let σs mkFreshExprMVar (σs.mkType u)
let P mkFreshExprMVar (mkApp (mkConst ``SPred [u]) σs)
let inst synthInstance (mkApp3 (mkConst ``PropAsSPredTautology [u]) goal σs P)
let u instantiateLevelMVars u

View File

@@ -33,14 +33,14 @@ def synthIsAnd (u : Level) (σs H : Expr) : OptionT MetaM (Expr × Expr × Expr)
-- Produce a proof for Q ∧ H ⊢ₛ T by opening a new goal P ⊢ₛ T, where P ⊣⊢ₛ Q ∧ H.
def mCasesAddGoal (u : Level) (goals : IO.Ref (Array MVarId)) (σs : Expr) (T : Expr) (Q : Expr) (H : Expr) : MetaM (Unit × MGoal × Expr) := do
let (P, hand) := SPred.mkAnd u σs Q H
let (P, hand) := mkAnd u σs Q H
-- hand : Q ∧ H ⊣⊢ₛ P
-- Need to produce a proof that P ⊢ₛ T and return res
let goal : MGoal := { u := u, σs := σs, hyps := P, target := T }
let m mkFreshExprSyntheticOpaqueMVar goal.toExpr
goals.modify (·.push m.mvarId!)
let prf := mkApp7 (mkConst ``Cases.add_goal [u]) σs P Q H T hand m
let goal := { goal with hyps := SPred.mkAnd! u σs Q H }
let goal := { goal with hyps := mkAnd! u σs Q H }
return ((), goal, prf)
private def getQH (goal : MGoal) : MetaM (Expr × Expr) := do
@@ -62,7 +62,7 @@ def mCasesExists (H : Expr) (name : TSyntax ``binderIdent)
let (Q, _) getQH goal
let u getLevel α
let prf := mkApp6 (mkConst ``Cases.exists [goal.u, u]) σs α Q ψ goal.target ( mkLambdaFVars #[x] prf)
let goal := { goal with hyps := SPred.mkAnd! goal.u σs Q H }
let goal := { goal with hyps := mkAnd! goal.u σs Q H }
return (r, goal, prf)
-- goal is P ⊢ₛ T
@@ -84,7 +84,7 @@ partial def mCasesCore (u : Level) (σs : Expr) (H : Expr) (pat : MCasesPat) (k
-- prf : Q ∧ ⌜True⌝ ⊢ₛ T
-- Then Q ∧ H ⊢ₛ Q ∧ ⌜True⌝ ⊢ₛ T
let prf := mkApp5 (mkConst ``Cases.clear [u]) σs Q H goal.target prf
let goal := { goal with hyps := SPred.mkAnd! u σs Q H }
let goal := { goal with hyps := mkAnd! u σs Q H }
return (a, goal, prf)
| .stateful name => do
let (name, ref) getFreshHypName name
@@ -129,7 +129,7 @@ partial def mCasesCore (u : Level) (σs : Expr) (H : Expr) (pat : MCasesPat) (k
-- 8. Reassociate to Q ∧ (H₁ ∧ H₂) ⊢ₛ T, rebuild Q ∧ H ⊢ₛ T and return it.
let ((a, Q), goal, prf) mCasesCore u σs H₁ p fun H₁' => do
let ((a, Q), goal, prf) mCasesCore u σs H₂ (.tuple ps) fun H₂' => do
let (H₁₂', hand') := SPred.mkAnd u σs H₁' H₂'
let (H₁₂', hand') := mkAnd u σs H₁' H₂'
let (a, goal, prf) k H₁₂' -- (2)
-- (3) prf : Q ∧ H₁₂' ⊢ₛ T
-- (4) refocus to (Q ∧ H₁') ∧ H₂'
@@ -137,19 +137,19 @@ partial def mCasesCore (u : Level) (σs : Expr) (H : Expr) (pat : MCasesPat) (k
let T := goal.target
let prf := mkApp8 (mkConst ``Cases.and_1 [u]) σs Q H₁' H₂' H₁₂' T hand' prf
-- check prf
let QH₁' := SPred.mkAnd! u σs Q H₁'
let goal := { goal with hyps := SPred.mkAnd! u σs QH₁' H₂' }
let QH₁' := mkAnd! u σs Q H₁'
let goal := { goal with hyps := mkAnd! u σs QH₁' H₂' }
return ((a, Q), goal, prf)
-- (5) prf : (Q ∧ H₁') ∧ H₂ ⊢ₛ T
-- (6) refocus to prf : (Q ∧ H₂) ∧ H₁' ⊢ₛ T
let prf := mkApp6 (mkConst ``Cases.and_2 [u]) σs Q H₁' H₂ goal.target prf
let QH₂ := SPred.mkAnd! u σs Q H₂
let goal := { goal with hyps := SPred.mkAnd! u σs QH₂ H₁' }
let QH₂ := mkAnd! u σs Q H₂
let goal := { goal with hyps := mkAnd! u σs QH₂ H₁' }
return ((a, Q), goal, prf)
-- (7) prf : (Q ∧ H₂) ∧ H₁ ⊢ₛ T
-- (8) rearrange to Q ∧ H ⊢ₛ T
let prf := mkApp8 (mkConst ``Cases.and_3 [u]) σs Q H₁ H₂ H goal.target hand prf
let goal := { goal with hyps := SPred.mkAnd! u σs Q H }
let goal := { goal with hyps := mkAnd! u σs Q H }
return (a, goal, prf)
else if let some (_α, σs, ψ) := H.consumeMData.app3? ``SPred.exists then
let .one n := p
@@ -171,7 +171,7 @@ partial def mCasesCore (u : Level) (σs : Expr) (H : Expr) (pat : MCasesPat) (k
let (_a, goal₁, prf₁) mCasesCore u σs H₁ p k
let (a, _goal₂, prf₂) mCasesCore u σs H₂ (.alts ps) k
let (Q, _H₁) getQH goal₁
let goal := { goal₁ with hyps := SPred.mkAnd! u σs Q (mkApp3 (mkConst ``SPred.or [u]) σs H₁ H₂) }
let goal := { goal₁ with hyps := mkAnd! u σs Q (mkApp3 (mkConst ``SPred.or [u]) σs H₁ H₂) }
let prf := mkApp7 (mkConst ``SPred.and_or_elim_r [u]) σs Q H₁ H₂ goal.target prf₁ prf₂
return (a, goal, prf)

View File

@@ -34,11 +34,11 @@ partial def focusHyp (u : Level) (σs : Expr) (e : Expr) (name : Name) : Option
try
-- NB: Need to prefer rhs over lhs, like the goal view (Lean.Elab.Tactic.Do.ProofMode.Delab).
let focus, rhs', h₁ focusHyp u σs rhs name
let C, h₂ := SPred.mkAnd u σs lhs rhs'
let C, h₂ := mkAnd u σs lhs rhs'
return focus, C, mkApp8 (mkConst ``Focus.right [u]) σs lhs rhs rhs' C focus h₁ h₂
catch _ =>
let focus, lhs', h₁ focusHyp u σs lhs name
let C, h₂ := SPred.mkAnd u σs lhs' rhs
let C, h₂ := mkAnd u σs lhs' rhs
return focus, C, mkApp8 (mkConst ``Focus.left [u]) σs lhs lhs' rhs C focus h₁ h₂
else if let some _ := parseEmptyHyp? e then
none
@@ -49,18 +49,18 @@ def MGoal.focusHyp (goal : MGoal) (name : Name) : Option FocusResult :=
Lean.Elab.Tactic.Do.ProofMode.focusHyp goal.u goal.σs goal.hyps name
def FocusResult.refl (u : Level) (σs : Expr) (restHyps : Expr) (focusHyp : Expr) : FocusResult :=
let proof := mkApp2 (mkConst ``SPred.bientails.refl [u]) σs (SPred.mkAnd! u σs restHyps focusHyp)
let proof := mkApp2 (mkConst ``SPred.bientails.refl [u]) σs (mkAnd! u σs restHyps focusHyp)
{ restHyps, focusHyp, proof }
def FocusResult.restGoal (res : FocusResult) (goal : MGoal) : MGoal :=
{ goal with hyps := res.restHyps }
def FocusResult.recombineGoal (res : FocusResult) (goal : MGoal) : MGoal :=
{ goal with hyps := SPred.mkAnd! goal.u goal.σs res.restHyps res.focusHyp }
{ goal with hyps := mkAnd! goal.u goal.σs res.restHyps res.focusHyp }
/-- Turn a proof for `(res.recombineGoal goal).toExpr` into one for `goal.toExpr`. -/
def FocusResult.rewriteHyps (res : FocusResult) (goal : MGoal) : Expr Expr :=
mkApp6 (mkConst ``Focus.rewrite_hyps [goal.u]) goal.σs goal.hyps (SPred.mkAnd! goal.u goal.σs res.restHyps res.focusHyp) goal.target res.proof
mkApp6 (mkConst ``Focus.rewrite_hyps [goal.u]) goal.σs goal.hyps (mkAnd! goal.u goal.σs res.restHyps res.focusHyp) goal.target res.proof
def MGoal.focusHypWithInfo (goal : MGoal) (name : Ident) : MetaM FocusResult := do
let some res := goal.focusHyp name.getId | throwError "unknown hypothesis '{name}'"

View File

@@ -37,7 +37,7 @@ partial def transferHypNames (P P' : Expr) : MetaM Expr := (·.snd) <$> label (c
if let some (u, σs, L, R) := parseAnd? P' then
let (Ps, L') label Ps L
let (Ps, R') label Ps R
return (Ps, SPred.mkAnd! u σs L' R')
return (Ps, mkAnd! u σs L' R')
else
let mut Ps' := Ps
repeat
@@ -52,25 +52,25 @@ partial def transferHypNames (P P' : Expr) : MetaM Expr := (·.snd) <$> label (c
unreachable!
def mFrameCore [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m]
(goal : MGoal) (kFail : m Expr) (kSuccess : Expr /-φ:Prop-/ Expr /-h:φ-/ MGoal m Expr) : m Expr := do
(goal : MGoal) (kFail : m (α × Expr)) (kSuccess : Expr /-φ:Prop-/ Expr /-h:φ-/ MGoal m (α × Expr)) : m (α × Expr) := do
let P := goal.hyps
let φ mkFreshExprMVar (mkSort .zero)
let P' mkFreshExprMVar (mkApp (mkConst ``SPred [goal.u]) goal.σs)
if let .some inst trySynthInstance (mkApp4 (mkConst ``HasFrame [goal.u]) goal.σs P P' φ) then
if let some inst synthInstance? (mkApp4 (mkConst ``HasFrame [goal.u]) goal.σs P P' φ) then
if isDefEq (mkConst ``True) φ then return ( kFail)
-- copy the name of P to P' if it is a named hypothesis
let P' transferHypNames P P'
let goal := { goal with hyps := P' }
withLocalDeclD ( liftMetaM <| mkFreshUserName `h) φ fun => do
let prf kSuccess φ goal
let (a, prf) kSuccess φ goal
let prf mkLambdaFVars #[] prf
let prf := mkApp7 (mkConst ``Frame.frame [goal.u]) goal.σs P P' goal.target φ inst prf
return prf
return (a, prf)
else
kFail
def mTryFrame [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m]
(goal : MGoal) (k : MGoal m Expr) : m Expr :=
(goal : MGoal) (k : MGoal m (α × Expr)) : m (α × Expr) :=
mFrameCore goal (k goal) (fun _ _ goal => k goal)
@[builtin_tactic Lean.Parser.Tactic.mframe]
@@ -79,8 +79,8 @@ def elabMFrame : Tactic | _ => do
mvar.withContext do
let g instantiateMVars <| mvar.getType
let some goal := parseMGoal? g | throwError "not in proof mode"
let prf mFrameCore goal (fun _ => throwError "Could not infer frame") fun _ _ goal => do
let (m, prf) mFrameCore goal (fun _ => throwError "Could not infer frame") fun _ _ goal => do
let m mkFreshExprSyntheticOpaqueMVar goal.toExpr
replaceMainGoal [m.mvarId!]
return m
return (m, m)
mvar.assign prf
replaceMainGoal [m.mvarId!]

View File

@@ -30,7 +30,7 @@ def elabMDup : Tactic
addHypInfo h goal.σs hyp (isBinder := true)
let H' := hyp.toExpr
let T := goal.target
let newGoal := { goal with hyps := SPred.mkAnd! goal.u goal.σs P H' }
let newGoal := { goal with hyps := mkAnd! goal.u goal.σs P H' }
let m mkFreshExprSyntheticOpaqueMVar newGoal.toExpr
mvar.assign (mkApp7 (mkConst ``Have.dup [goal.u]) goal.σs P Q H T res.proof m)
replaceMainGoal [m.mvarId!]
@@ -52,7 +52,7 @@ def elabMHave : Tactic
addHypInfo h goal.σs hyp (isBinder := true)
let H := hyp.toExpr
let T := goal.target
let (PH, hand) := SPred.mkAnd goal.u goal.σs P H
let (PH, hand) := mkAnd goal.u goal.σs P H
let haveGoal := { goal with target := H }
let hhave elabTermEnsuringType rhs haveGoal.toExpr
let newGoal := { goal with hyps := PH }
@@ -82,7 +82,7 @@ def elabMReplace : Tactic
let haveGoal := { goal with target := H' }
let hhave elabTermEnsuringType rhs haveGoal.toExpr
let T := goal.target
let (PH', hand) := SPred.mkAnd goal.u goal.σs P H'
let (PH', hand) := mkAnd goal.u goal.σs P H'
let newGoal := { goal with hyps := PH' }
let m mkFreshExprSyntheticOpaqueMVar newGoal.toExpr
let prf := mkApp (mkApp10 (mkConst ``Have.replace [goal.u]) goal.σs P H H' PH PH' T res.proof hand hhave) m

View File

@@ -15,32 +15,32 @@ namespace Lean.Elab.Tactic.Do.ProofMode
open Std.Do SPred.Tactic
open Lean Elab Tactic Meta
partial def mIntro [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] (goal : MGoal) (ident : TSyntax ``binderIdent) (k : MGoal m Expr) : m Expr := do
partial def mIntro [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] (goal : MGoal) (ident : TSyntax ``binderIdent) (k : MGoal m (α × Expr)) : m (α × Expr) := do
if let some (σs, H, T) := goal.target.app3? ``SPred.imp then
let (name, ref) liftMetaM <| getFreshHypName ident
let uniq liftMetaM mkFreshId
let hyp := Hyp.mk name uniq H
addHypInfo ref σs hyp (isBinder := true)
let Q := goal.hyps
let H := hyp.toExpr
let (P, hand) := SPred.mkAnd goal.u goal.σs goal.hyps H
let prf k { goal with hyps := P, target := T }
let prf := mkApp7 (mkConst ``Intro.intro [goal.u]) σs P Q H T hand prf
return prf
let (name, ref) liftMetaM <| getFreshHypName ident
let uniq liftMetaM mkFreshId
let hyp := Hyp.mk name uniq H
addHypInfo ref σs hyp (isBinder := true)
let Q := goal.hyps
let H := hyp.toExpr
let (P, hand) := mkAnd goal.u goal.σs goal.hyps H
let (a, prf) k { goal with hyps := P, target := T }
let prf := mkApp7 (mkConst ``Intro.intro [goal.u]) σs P Q H T hand prf
return (a, prf)
else if let .letE name type val body _nondep := goal.target then
let name match ident with
| `(binderIdent| $name:ident) => pure name.getId
| `(binderIdent| $_) => liftMetaM <| mkFreshUserName name
-- Even if `_nondep = true` we want to retain the value of the let binding for the proof.
withLetDecl name type val (nondep := false) fun val => do
let prf k { goal with target := body.instantiate1 val }
let (a, prf) k { goal with target := body.instantiate1 val }
let prf liftMetaM <| mkLetFVars #[val] prf
return prf
return (a, prf)
else
liftMetaM <| throwError "Target not an implication or let-binding {goal.target}"
-- This is regular MVar.intro, but it takes care not to leave the proof mode by preserving metadata
partial def mIntroForall [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] (goal : MGoal) (ident : TSyntax ``binderIdent) (k : MGoal m Expr) : m Expr :=
partial def mIntroForall [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] (goal : MGoal) (ident : TSyntax ``binderIdent) (k : MGoal m (α × Expr)) : m (α × Expr) :=
controlAt MetaM fun map => do
let some (_type, σ, σs') := ( whnf goal.σs).app3? ``List.cons | liftMetaM <| throwError "Ambient state list not a cons {goal.σs}"
let name match ident with
@@ -48,17 +48,17 @@ partial def mIntroForall [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m]
| `(binderIdent| $_) => liftMetaM <| mkFreshUserName `s
withLocalDeclD name σ fun s => do
addLocalVarInfo ident ( getLCtx) s σ (isBinder := true)
let H := pushForallContextIntoHyps σs' (mkApp goal.hyps s)
let H := betaRevPreservingHypNames σs' goal.hyps #[s]
let T := goal.target.betaRev #[s]
map do
let prf k { u := goal.u, σs:=σs', hyps:=H, target:=T }
let (a, prf) k { u := goal.u, σs:=σs', hyps:=H, target:=T }
let prf mkLambdaFVars #[s] prf
return mkApp5 (mkConst ``SPred.entails_cons_intro [goal.u]) σs' σ goal.hyps goal.target prf
return (a, mkApp5 (mkConst ``SPred.entails_cons_intro [goal.u]) σs' σ goal.hyps goal.target prf)
def mIntroForallN [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] (goal : MGoal) (n : Nat) (k : MGoal m Expr) : m Expr :=
def mIntroForallN [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m] (goal : MGoal) (n : Nat) (k : MGoal m (α × Expr)) : m (α × Expr) :=
match n with
| 0 => k goal
| n+1 => do mIntroForall goal ( liftMetaM `(binderIdent| _)) fun g =>
| n+1 => do mIntroForall goal ( liftM (m := MetaM) `(binderIdent| _)) fun g =>
mIntroForallN g n k
macro_rules
@@ -76,18 +76,18 @@ def elabMIntro : Tactic
let (mvar, goal) mStartMVar ( getMainGoal)
mvar.withContext do
let goals IO.mkRef []
mvar.assign ( mIntro goal ident fun newGoal => do
mvar.assign ( Prod.snd <$> mIntro goal ident fun newGoal => do
let m mkFreshExprSyntheticOpaqueMVar newGoal.toExpr
goals.modify (m.mvarId! :: ·)
return m)
return ((), m))
replaceMainGoal ( goals.get)
| `(tactic| mintro $ident:binderIdent) => do
let (mvar, goal) mStartMVar ( getMainGoal)
mvar.withContext do
let goals IO.mkRef []
mvar.assign ( mIntroForall goal ident fun newGoal => do
mvar.assign ( Prod.snd <$> mIntroForall goal ident fun newGoal => do
let m mkFreshExprSyntheticOpaqueMVar newGoal.toExpr
goals.modify (m.mvarId! :: ·)
return m)
return ((), m))
replaceMainGoal ( goals.get)
| _ => throwUnsupportedSyntax

View File

@@ -35,36 +35,13 @@ def parseHyp? : Expr → Option Hyp
def Hyp.toExpr (hyp : Hyp) : Expr :=
.mdata [(nameAnnotation, .ofName hyp.name), (uniqAnnotation, .ofName hyp.uniq)] hyp.p
def SPred.mkType (u : Level) (σs : Expr) : Expr :=
mkApp (mkConst ``SPred [u]) σs
-- set_option pp.all true in
-- #check ⌜True⌝
def SPred.mkPure (u : Level) (σs : Expr) (p : Expr) : Expr :=
mkApp3 (mkConst ``SVal.curry [u]) (mkApp (mkConst ``ULift [u, 0]) (.sort .zero)) σs <|
mkLambda `tuple .default (mkApp (mkConst ``SVal.StateTuple [u]) σs) <|
mkApp2 (mkConst ``ULift.up [u, 0]) (.sort .zero) (Expr.liftLooseBVars p 0 1)
def SPred.isPure? : Expr Option (Level × Expr × Expr)
| mkApp3 (.const ``SVal.curry [u]) (mkApp (.const ``ULift _) (.sort .zero)) σs <|
.lam _ _ (mkApp2 (.const ``ULift.up _) _ p) _ => some (u, σs, (Expr.lowerLooseBVars p 0 1))
| _ => none
def emptyHypName := `emptyHyp
def emptyHyp (u : Level) (σs : Expr) : Expr := -- ⌜True⌝ standing in for an empty conjunction of hypotheses
Hyp.toExpr { name := emptyHypName, uniq := emptyHypName, p := SPred.mkPure u σs (mkConst ``True) }
def parseEmptyHyp? (e : Expr) : Option (Level × Expr) := do
let h parseHyp? e
unless h.name == emptyHypName || h.name.hasMacroScopes do
-- Interpret empty hyps when they are not named `emptyHyp` or have macro scopes
-- (= introduced inaccessibly). Otherwise we want to treat it as a regular hypothesis.
failure
let (u, σs, p) SPred.isPure? h.p
match p with
| .const ``True _ => return (u, σs)
| _ => failure
mkApp3 (mkConst ``SVal.curry [u]) (mkApp (mkConst ``ULift [u, 0]) (.sort .zero)) σs <| mkLambda `tuple .default (mkApp (mkConst ``SVal.StateTuple [u]) σs) (mkApp2 (mkConst ``ULift.up [u, 0]) (.sort .zero) (mkConst ``True))
def parseEmptyHyp? : Expr Option (Level × Expr)
| mkApp3 (.const ``SVal.curry [u]) (mkApp (.const ``ULift _) (.sort .zero)) σs (.lam _ _ (mkApp2 (.const ``ULift.up _) _ (.const ``True _)) _) => some (u, σs)
| _ => none
def pushLeftConjunct (pos : SubExpr.Pos) : SubExpr.Pos :=
pos.pushNaryArg 3 1
@@ -74,27 +51,26 @@ def pushRightConjunct (pos : SubExpr.Pos) : SubExpr.Pos :=
/-- Combine two hypotheses into a conjunction.
Precondition: Neither `lhs` nor `rhs` is empty (`parseEmptyHyp?`). -/
def SPred.mkAnd! (u : Level) (σs lhs rhs : Expr) : Expr :=
def mkAnd! (u : Level) (σs lhs rhs : Expr) : Expr :=
mkApp3 (mkConst ``SPred.and [u]) σs lhs rhs
/-- Smart constructor that cancels away empty hypotheses,
along with a proof that `lhs ∧ rhs ⊣⊢ₛ result`. -/
def SPred.mkAnd (u : Level) (σs lhs rhs : Expr) : Expr × Expr :=
def mkAnd (u : Level) (σs lhs rhs : Expr) : Expr × Expr :=
if let some _ := parseEmptyHyp? lhs then
(rhs, mkApp2 (mkConst ``SPred.true_and [u]) σs rhs)
else if let some _ := parseEmptyHyp? rhs then
(lhs, mkApp2 (mkConst ``SPred.and_true [u]) σs lhs)
else
let result := SPred.mkAnd! u σs lhs rhs
let result := mkAnd! u σs lhs rhs
(result, mkApp2 (mkConst ``SPred.bientails.refl [u]) σs result)
def TypeList.mkType (u : Level) : Expr := mkApp (mkConst ``List [.succ u]) (mkSort (.succ u))
def TypeList.mkNil (u : Level) : Expr := mkApp (mkConst ``List.nil [.succ u]) (mkSort (.succ u))
def TypeList.mkCons (u : Level) (hd tl : Expr) : Expr := mkApp3 (mkConst ``List.cons [.succ u]) (mkSort (.succ u)) hd tl
def σs.mkType (u : Level) : Expr := mkApp (mkConst ``List [.succ u]) (mkSort (.succ u))
def σs.mkNil (u : Level) : Expr := mkApp (mkConst ``List.nil [.succ u]) (mkSort (.succ u))
def parseAnd? (e : Expr) : Option (Level × Expr × Expr × Expr) :=
(e.getAppFn.constLevels![0]!, ·) <$> e.app3? ``SPred.and
<|> (0, TypeList.mkNil 0, ·) <$> e.app2? ``And
<|> (0, σs.mkNil 0, ·) <$> e.app2? ``And
structure MGoal where
u : Level
@@ -151,30 +127,19 @@ def getFreshHypName : TSyntax ``binderIdent → CoreM (Name × Syntax)
| `(binderIdent| $name:ident) => pure (name.getId, name)
| stx => return ( mkFreshUserName `h, stx)
partial def pushForallContextIntoHyps (σs hyps : Expr) : Expr := go #[] #[] hyps
where
wrap (revLams : Array (Name × Expr × BinderInfo)) (revAppArgs : Array Expr) (body : Expr) : Expr :=
revLams.foldr (fun (x, ty, info) e => .lam x ty e info) (body.betaRev revAppArgs)
go (revLams : Array (Name × Expr × BinderInfo)) (revAppArgs : Array Expr) (e : Expr) : Expr :=
if let some (u, _σs) := parseEmptyHyp? e then
emptyHyp u σs
else if let some hyp := parseHyp? e then
{ hyp with p := wrap revLams revAppArgs hyp.p }.toExpr
else if let some (u, _σs, lhs, rhs) := parseAnd? e then
SPred.mkAnd! u σs (go revLams revAppArgs lhs) (go revLams revAppArgs rhs)
else if let .lam x ty body info := e then
if let some a := revAppArgs.back? then
go revLams revAppArgs.pop (body.instantiate1 a)
else
go (revLams.push (x, ty, info)) revAppArgs body
else if let .app f a := e then
go revLams (revAppArgs.push a) f
else
wrap revLams revAppArgs e
partial def betaRevPreservingHypNames (σs' e : Expr) (args : Array Expr) : Expr :=
if let some (u, _) := parseEmptyHyp? e then
emptyHyp u σs'
else if let some hyp := parseHyp? e then
{ hyp with p := hyp.p.betaRev args }.toExpr
else if let some (u, _σs, lhs, rhs) := parseAnd? e then
-- _σs = σ :: σs'
mkAnd! u σs' (betaRevPreservingHypNames σs' lhs args) (betaRevPreservingHypNames σs' rhs args)
else
e.betaRev args
def betaPreservingHypNames (σs' e : Expr) (args : Array Expr) : Expr :=
pushForallContextIntoHyps σs' (mkAppN e args)
betaRevPreservingHypNames σs' e args.reverse
def dropStateList (σs : Expr) (n : Nat) : MetaM Expr := do
let mut σs := σs
@@ -207,7 +172,7 @@ partial def MGoal.renameInaccessibleHyps (goal : MGoal) (idents : Array (TSyntax
if let some (u, σs, lhs, rhs) := parseAnd? H then
let rhs go rhs -- NB: First go right because those are the "most recent" hypotheses
let lhs go lhs
return SPred.mkAnd! u σs lhs rhs
return mkAnd! u σs lhs rhs
return H
def addLocalVarInfo (stx : Syntax) (lctx : LocalContext)

View File

@@ -32,7 +32,7 @@ def mPureCore (σs : Expr) (hyp : Expr) (name : TSyntax ``binderIdent)
let (a, goal, prf /- : goal.toExpr -/) k φ h
let prf mkLambdaFVars #[h] prf
let prf := mkApp7 (mkConst ``Pure.thm [u]) σs goal.hyps hyp goal.target φ inst prf
let goal := { goal with hyps := SPred.mkAnd! u σs goal.hyps hyp }
let goal := { goal with hyps := mkAnd! u σs goal.hyps hyp }
return (a, goal, prf)
@[builtin_tactic Lean.Parser.Tactic.mpure]

View File

@@ -16,9 +16,7 @@ namespace Lean.Elab.Tactic.Do.ProofMode
open Std.Do SPred.Tactic
open Lean Elab Tactic Meta
variable {m : Type Type u} [Monad m] [MonadControlT MetaM m] [MonadLiftT MetaM m]
partial def mRevert (goal : MGoal) (ref : TSyntax `ident) (k : MGoal m Expr) : m Expr := do
partial def mRevertStep (goal : MGoal) (ref : TSyntax `ident) (k : MGoal MetaM Expr) : MetaM Expr := do
let res goal.focusHypWithInfo ref
let P := goal.hyps
let Q := res.restHyps
@@ -28,78 +26,14 @@ partial def mRevert (goal : MGoal) (ref : TSyntax `ident) (k : MGoal → m Expr)
let prf := mkApp7 (mkConst ``Revert.revert [goal.u]) goal.σs P Q H T res.proof prf
return prf
/--
Turn goal
hᵢ : Hᵢ
⊢ₛ T e₁ ... eₙ
into
hᵢ : fun sₙ ... s₁ => Hᵢ
h : fun sₙ ... s₁ => ⌜s₁ = e₁ ∧ ... ∧ sₙ = eₙ⌝
⊢ₛ T
-/
partial def mRevertForallN (goal : MGoal) (n : Nat) (hypName : Name) (k : MGoal m Expr) : m Expr := do
if n = 0 then return k goal
let H := goal.hyps
let T := goal.target.consumeMData
let f := T.getAppFn
let args := T.getAppRevArgs
let revertArgs := args[0:n].toArray.reverse
unless revertArgs.size = n do
liftMetaM <| throwError "mrevert: expected {n} excess arguments in {T}, got {revertArgs.size}"
let revertArgsTypes liftMetaM <| revertArgs.mapM inferType
let declInfos revertArgsTypes.mapIdxM fun i ty => do
return (( liftMetaM <| mkFreshUserName `s).appendIndexAfter (i+1), ty)
-- Build `fun s₁ ... sₙ => H ∧ ⌜s₁ = e₁ ∧ ... ∧ sₙ = eₙ⌝`
let (H, φ) withLocalDeclsDND declInfos fun ss => do
let eqs (revertArgs.zip ss).mapM fun (e, s) => mkEq s e
let eqs := eqs.toList
let φ := mkAndN eqs
let φ := SPred.mkPure goal.u goal.σs φ
let uniq liftMetaM <| mkFreshUserName hypName
let φ := Hyp.toExpr hypName, uniq, mkLambdaFVars ss φ
return ( mkLambdaFVars ss H, φ)
-- Build `⟨rfl, ..., rfl⟩ : e₁ = e₁ ∧ ... ∧ eₙ = eₙ`
let prfs liftMetaM <| revertArgs.mapM mkEqRefl
let h mkAndIntroN prfs.toList
-- Push `fun s₁ ... sₙ =>` into the hyps in `H`
let u := goal.u
let σs' := revertArgsTypes.foldr (TypeList.mkCons u) goal.σs
let H instantiateMVarsIfMVarApp H
let H := pushForallContextIntoHyps σs' H
let (H, hand) := SPred.mkAnd u σs' H φ
-- Prove `((fun s₁ ... sₙ => H) ∧ (fun s₁ ... sₙ => ⌜s₁ = e₁ ∧ ... ∧ sₙ = eₙ⌝)) ⊢ₛ T`
let goal' := { u, σs := σs', hyps := H, target := mkAppRev f args[n:] }
let prf k goal'
-- Build the proof for `H ⊢ₛ T e₁ ... eₙ`
let prf := mkApp8 (mkConst ``Revert.and_pure_intro_r [goal.u]) goal.σs ( inferType h) goal.hyps (mkAppN H revertArgs) goal.target h (mkAppN hand revertArgs) (mkAppN prf revertArgs)
-- goal.checkProof prf
return prf
@[builtin_tactic Lean.Parser.Tactic.mrevert]
def elabMRevert : Tactic
| `(tactic| mrevert $h:ident) => do
let mvar getMainGoal
let some goal := parseMGoal? ( mvar.getType)
| throwError "Not in proof mode"
mvar.withContext do
let goals IO.mkRef []
mvar.assign ( mRevert goal h fun newGoal => do
let m mkFreshExprSyntheticOpaqueMVar newGoal.toExpr
goals.modify (m.mvarId! :: ·)
return m)
replaceMainGoal ( goals.get)
| `(tactic| mrevert $[$n]?) => do
| `(tactic| mrevert $h) => do
let (mvar, goal) mStartMVar ( getMainGoal)
mvar.withContext do
let n := ((·.getNat) <$> n).getD 1
let goals IO.mkRef []
mvar.assign ( mRevertForallN goal n ( mkFreshUserName `h) fun newGoal => do
mvar.assign ( mRevertStep goal h fun newGoal => do
let m mkFreshExprSyntheticOpaqueMVar newGoal.toExpr
goals.modify (m.mvarId! :: ·)
return m)

View File

@@ -25,7 +25,7 @@ def mSpecializeImpStateful (P : Expr) (QR : Expr) (arg : TSyntax `term) : Option
guard (arg.raw.isIdent)
let some specHyp := parseHyp? QR | failure
let mkApp3 (.const ``SPred.imp [u]) σs Q' R := specHyp.p | failure
let some argRes := focusHyp u σs (SPred.mkAnd! u σs P QR) arg.raw.getId | failure
let some argRes := focusHyp u σs (mkAnd! u σs P QR) arg.raw.getId | failure
let some hyp := parseHyp? argRes.focusHyp | failure
addHypInfo arg σs hyp
OptionT.mk do -- no OptionT failure after this point
@@ -37,7 +37,7 @@ def mSpecializeImpStateful (P : Expr) (QR : Expr) (arg : TSyntax `term) : Option
let hrefocus := argRes.proof -- P ∧ (Q → R) ⊣⊢ₛ P' ∧ Q
let proof := mkApp6 (mkConst ``Specialize.imp_stateful [u]) σs P P' Q R hrefocus
-- check proof
trace[Meta.Tactic.Do.specialize] "Statefully specialize {specHyp.p} with {Q}. New Goal: {SPred.mkAnd! u σs P R}"
trace[Meta.Tactic.Do.specialize] "Statefully specialize {specHyp.p} with {Q}. New Goal: {mkAnd! u σs P R}"
unless isDefEq Q Q' do
throwError "failed to specialize {specHyp.p} with {Q}"
@@ -67,22 +67,22 @@ def mSpecializeImpPure (P : Expr) (QR : Expr) (arg : TSyntax `term) : OptionT Ta
pushGoals mvarIds
let proof := mkApp7 (mkConst ``Specialize.imp_pure [u]) σs φ P Q R inst
-- check proof
trace[Meta.Tactic.Do.specialize] "Purely specialize {specHyp.p} with {Q}. New Goal: {SPred.mkAnd! u σs P R}"
trace[Meta.Tactic.Do.specialize] "Purely specialize {specHyp.p} with {Q}. New Goal: {mkAnd! u σs P R}"
-- logInfo m!"proof: {← inferType proof}"
return ({ specHyp with p := R }.toExpr, proof)
def mSpecializeForall (P : Expr) (Ψ : Expr) (arg : TSyntax `term) : OptionT TacticM (Expr × Expr) := do
let some specHyp := parseHyp? Ψ | panic! "Precondition of specializeForall violated"
let mkApp3 (.const ``SPred.forall [uα, u]) α σs αR := specHyp.p | failure
let mkApp3 (.const ``SPred.forall [u, v]) α σs αR := specHyp.p | failure
let (a, mvarIds) try
elabTermWithHoles arg.raw α `specialize (allowNaturalHoles := true)
catch _ => failure
OptionT.mk do -- no OptionT failure after this point
pushGoals mvarIds
let proof := mkApp5 (mkConst ``Specialize.forall [uα, u]) α σs αR P a
let proof := mkApp5 (mkConst ``Specialize.forall [u, v]) σs α P αR a
let R := αR.beta #[a]
-- check proof
trace[Meta.Tactic.Do.specialize] "Instantiate {specHyp.p} with {a}. New Goal: {SPred.mkAnd! u σs P R}"
trace[Meta.Tactic.Do.specialize] "Instantiate {specHyp.p} with {a}. New Goal: {mkAnd! u σs P R}"
return ({ specHyp with p := R }.toExpr, proof)
@[builtin_tactic Lean.Parser.Tactic.mspecialize]
@@ -97,8 +97,8 @@ def elabMSpecialize : Tactic
-- 2. Produce a (transitive chain of) proofs
-- P' ∧ H ⊢ P' ∧ H₁ ⊢ₛ P' ∧ H₂ ⊢ₛ ...
-- One for each arg; end up with goal P' ∧ H' ⊢ₛ T
-- 3. Recombine with SPred.mkAnd (NB: P' might be empty), compose with P' ∧ H' ⊣⊢ₛ SPred.mkAnd P' H'.
-- 4. Make a new MVar for goal `SPred.mkAnd P' H' ⊢ T` and assign the transitive chain.
-- 3. Recombine with mkAnd (NB: P' might be empty), compose with P' ∧ H' ⊣⊢ₛ mkAnd P' H'.
-- 4. Make a new MVar for goal `mkAnd P' H' ⊢ T` and assign the transitive chain.
let some specFocus := goal.focusHyp hyp.getId | throwError "unknown identifier '{hyp}'"
let u := goal.u
let σs := goal.σs
@@ -106,7 +106,7 @@ def elabMSpecialize : Tactic
let mut H := specFocus.focusHyp
let some hyp' := parseHyp? H | panic! "Invariant of specialize violated"
addHypInfo hyp σs hyp'
-- invariant: proof (_ : { goal with hyps := SPred.mkAnd! σs P H }.toExpr) fills the mvar
-- invariant: proof (_ : { goal with hyps := mkAnd! σs P H }.toExpr) fills the mvar
let mut proof : Expr Expr :=
mkApp7 (mkConst ``Specialize.focus [u]) σs goal.hyps P H goal.target specFocus.proof
@@ -118,12 +118,12 @@ def elabMSpecialize : Tactic
match res? with
| some (H', H2H') =>
-- logInfo m!"H: {H}, proof: {← inferType H2H'}"
proof := fun hgoal => proof (mkApp6 (mkConst ``SPred.entails.trans [u]) σs (SPred.mkAnd! u σs P H) (SPred.mkAnd! u σs P H') goal.target H2H' hgoal)
proof := fun hgoal => proof (mkApp6 (mkConst ``SPred.entails.trans [u]) σs (mkAnd! u σs P H) (mkAnd! u σs P H') goal.target H2H' hgoal)
H := H'
| none =>
throwError "Could not specialize {H} with {arg}"
let newMVar mkFreshExprSyntheticOpaqueMVar { goal with hyps := SPred.mkAnd! u σs P H }.toExpr
let newMVar mkFreshExprSyntheticOpaqueMVar { goal with hyps := mkAnd! u σs P H }.toExpr
mvar.assign (proof newMVar)
replaceMainGoal [newMVar.mvarId!]
@@ -143,8 +143,8 @@ def elabMspecializePure : Tactic
-- Produce a (transitive chain of) proofs
-- P ∧ H ⊢ P ∧ H₁ ⊢ₛ P ∧ H₂ ⊢ₛ ...
-- One for each arg; end up with goal P ∧ H' ⊢ₛ T
-- 3. Recombine with SPred.mkAnd (NB: P' might be empty), compose with P' ∧ H' ⊣⊢ₛ SPred.mkAnd P' H'.
-- 4. Make a new MVar for goal `SPred.mkAnd P' H' ⊢ T` and assign the transitive chain.
-- 3. Recombine with mkAnd (NB: P' might be empty), compose with P' ∧ H' ⊣⊢ₛ mkAnd P' H'.
-- 4. Make a new MVar for goal `mkAnd P' H' ⊢ T` and assign the transitive chain.
let u := goal.u
let σs := goal.σs
let P := goal.hyps
@@ -156,8 +156,8 @@ def elabMspecializePure : Tactic
let uniq mkFreshId
let mut H := (Hyp.mk hyp.getId uniq ( instantiateMVars H)).toExpr
let goal : MGoal := { goal with hyps := SPred.mkAnd! u σs P H }
-- invariant: proof (_ : { goal with hyps := SPred.mkAnd! u σs P H }.toExpr) fills the mvar
let goal : MGoal := { goal with hyps := mkAnd! u σs P H }
-- invariant: proof (_ : { goal with hyps := mkAnd! u σs P H }.toExpr) fills the mvar
let mut proof : Expr Expr :=
mkApp8 (mkConst ``Specialize.pure_start [u]) σs φ H P T inst
@@ -169,7 +169,7 @@ def elabMspecializePure : Tactic
match res? with
| some (H', H2H') =>
-- logInfo m!"H: {H}, proof: {← inferType H2H'}"
proof := fun hgoal => proof (mkApp6 (mkConst ``SPred.entails.trans [u]) σs (SPred.mkAnd! u σs P H) (SPred.mkAnd! u σs P H') goal.target H2H' hgoal)
proof := fun hgoal => proof (mkApp6 (mkConst ``SPred.entails.trans [u]) σs (mkAnd! u σs P H) (mkAnd! u σs P H') goal.target H2H' hgoal)
H := H'
| none =>
throwError "Could not specialize {H} with {arg}"
@@ -177,7 +177,7 @@ def elabMspecializePure : Tactic
let some hyp' := parseHyp? H | panic! "Invariant of specialize_pure violated"
addHypInfo hyp σs hyp'
let newMVar mkFreshExprSyntheticOpaqueMVar { goal with hyps := SPred.mkAnd! u σs P H }.toExpr
let newMVar mkFreshExprSyntheticOpaqueMVar { goal with hyps := mkAnd! u σs P H }.toExpr
mvar.assign (proof newMVar)
replaceMainGoal [newMVar.mvarId!]
| _ => throwUnsupportedSyntax

View File

@@ -143,7 +143,7 @@ def mkPreTag (goalTag : Name) : Name := Id.run do
-/
def mSpec (goal : MGoal) (elabSpecAtWP : Expr n SpecTheorem) (goalTag : Name) (mkPreTag := mkPreTag) : n Expr := do
-- First instantiate `fun s => ...` in the target via repeated `mintro ∀s`.
mIntroForallN goal goal.target.consumeMData.getNumHeadLambdas fun goal => do
Prod.snd <$> mIntroForallN goal goal.target.consumeMData.getNumHeadLambdas fun goal => do
-- Elaborate the spec for the wp⟦e⟧ app in the target
let T := goal.target.consumeMData
unless T.getAppFn.constName! == ``PredTrans.apply do
@@ -188,7 +188,7 @@ def mSpec (goal : MGoal) (elabSpecAtWP : Expr → n SpecTheorem) (goalTag : Name
-- Often P or Q are schematic (i.e. an MVar app). Try to solve by rfl.
-- We do `fullApproxDefEq` here so that `constApprox` is active; this is useful in
-- `need_const_approx` of `doLogicTests.lean`.
let (HPRfl, QQ'Rfl) fullApproxDefEq <| do
let (HPRfl, QQ'Rfl) withAssignableSyntheticOpaque <| fullApproxDefEq <| do
return ( isDefEqGuarded P goal.hyps, isDefEqGuarded Q Q')
-- Discharge the validity proof for the spec if not rfl
@@ -214,7 +214,8 @@ def mSpec (goal : MGoal) (elabSpecAtWP : Expr → n SpecTheorem) (goalTag : Name
h (QQ'mono.betaRev excessArgs)
-- finally build the proof; HPPrf.trans (spec.trans QQ'mono)
return prePrf (postPrf spec)
let prf := prePrf (postPrf spec)
return ((), prf)
@[builtin_tactic Lean.Parser.Tactic.mspecNoBind]
def elabMSpecNoBind : Tactic

View File

@@ -6,12 +6,14 @@ Authors: Sebastian Graf
module
prelude
public import Init.Guard
public import Std.Do.WP
public import Std.Do.Triple
public import Lean.Meta.Tactic.Split
public import Lean.Elab.Tactic.Simp
public import Lean.Elab.Tactic.Meta
public import Lean.Elab.Tactic.Do.ProofMode.Basic
public import Lean.Elab.Tactic.Do.ProofMode.Intro
public import Lean.Elab.Tactic.Do.ProofMode.Revert
public import Lean.Elab.Tactic.Do.ProofMode.Cases
public import Lean.Elab.Tactic.Do.ProofMode.Specialize
public import Lean.Elab.Tactic.Do.ProofMode.Pure
@@ -19,8 +21,7 @@ public import Lean.Elab.Tactic.Do.LetElim
public import Lean.Elab.Tactic.Do.Spec
public import Lean.Elab.Tactic.Do.Attr
public import Lean.Elab.Tactic.Do.Syntax
public import Lean.Elab.Tactic.Do.VCGen.Basic
public import Lean.Elab.Tactic.Do.VCGen.Split
import Lean.Meta.Tactic.SplitIf
public section
@@ -29,36 +30,208 @@ namespace Lean.Elab.Tactic.Do
open Lean Parser Elab Tactic Meta Do ProofMode SpecAttr
open Std.Do
private def ProofMode.MGoal.withNewProg (goal : MGoal) (e : Expr) : MGoal :=
let wpApp := goal.target
let f := wpApp.getAppFn
let args := wpApp.getAppArgs
let wp := args[2]?
match wp with
| some (Expr.app rest _) => { goal with target := mkAppN f (args.set! 2 (mkApp rest e)) }
| _ => goal
builtin_initialize registerTraceClass `Elab.Tactic.Do.vcgen
namespace VCGen
register_builtin_option mvcgen.warning : Bool := {
defValue := true
group := "debug"
descr := "disable `mvcgen` usage warning"
}
partial def genVCs (goal : MVarId) (ctx : Context) (fuel : Fuel) : MetaM (Array MVarId) := do
let (mvar, goal) mStartMVar goal
mvar.withContext <| withReducible do
let (prf, state) StateRefT'.run (ReaderT.run (onGoal goal ( mvar.getTag)) ctx) { fuel }
mvar.assign prf
return state.vcs
inductive Fuel where
| limited (n : Nat)
| unlimited
deriving DecidableEq
structure Config where
/--
If true, do not substitute away let-declarations that are used at most once before starting
VC generation.
-/
noLetElim : Bool := false
declare_config_elab elabConfig Config
structure Context where
config : Config
specThms : SpecTheorems
simpCtx : Simp.Context
simprocs : Simp.SimprocsArray
structure State where
fuel : Fuel := .unlimited
simpState : Simp.State := {}
/--
The verification conditions that have been generated so far.
Includes `Type`-valued goals arising from instantiation of specifications.
-/
vcs : Array MVarId := #[]
abbrev VCGenM := ReaderT Context (StateRefT State MetaM)
def burnOne : VCGenM PUnit := do
let s get
match s.fuel with
| Fuel.limited 0 => return ()
| Fuel.limited (n + 1) => set { s with fuel := .limited n }
| Fuel.unlimited => return ()
def ifOutOfFuel (x : VCGenM α) (k : VCGenM α) : VCGenM α := do
let s get
match s.fuel with
| Fuel.limited 0 => x
| _ => k
def emitVC (subGoal : Expr) (name : Name) : VCGenM Expr := do
let m liftM <| mkFreshExprSyntheticOpaqueMVar subGoal (tag := name)
modify fun s => { s with vcs := s.vcs.push m.mvarId! }
return m
def addSubGoalAsVC (goal : MVarId) : VCGenM PUnit := do
modify fun s => { s with vcs := s.vcs.push goal }
def liftSimpM (x : SimpM α) : VCGenM α := do
let ctx read
let s get
let mref := (Simp.mkDefaultMethodsCore ctx.simprocs).toMethodsRef
let (a, simpState) x mref ctx.simpCtx |>.run s.simpState
set { s with simpState }
return a
instance : MonadLift SimpM VCGenM where
monadLift x := liftSimpM x
private def mkSpecContext (optConfig : Syntax) (lemmas : Syntax) (ignoreStarArg := false) : TacticM Context := do
let config elabConfig optConfig
let mut specThms getSpecTheorems
let mut simpStuff := #[]
let mut starArg := false
for arg in lemmas[1].getSepArgs do
if arg.getKind == ``simpErase then
try
-- Try and build SpecTheorems for the lemma to erase to see if it's
-- meant to be interpreted by SpecTheorems. Otherwise fall back to SimpTheorems.
let specThm
if let some fvar Term.isLocalIdent? arg[1] then
mkSpecTheoremFromLocal fvar.fvarId!
else
let id := arg[1]
if let .ok declName observing (realizeGlobalConstNoOverloadWithInfo id) then
mkSpecTheoremFromConst declName
else
withRef id <| throwUnknownConstant id.getId.eraseMacroScopes
specThms := specThms.eraseCore specThm.proof
catch _ =>
simpStuff := simpStuff.push arg -- simp tracks its own erase stuff
else if arg.getKind == ``simpLemma then
unless arg[0].isNone && arg[1].isNone do
-- When there is ←, →, ↑ or ↓ then this is for simp
simpStuff := simpStuff.push arg
continue
let term := arg[2]
match Term.resolveId? term (withInfo := true) <|> Term.elabCDotFunctionAlias? term with
| some (.const declName _) =>
let info getConstInfo declName
try
let thm mkSpecTheoremFromConst declName
specThms := addSpecTheoremEntry specThms thm
catch _ =>
simpStuff := simpStuff.push arg
| some (.fvar fvar) =>
let decl getFVarLocalDecl (.fvar fvar)
try
let thm mkSpecTheoremFromLocal fvar
specThms := addSpecTheoremEntry specThms thm
catch _ =>
simpStuff := simpStuff.push arg
| _ => withRef term <| throwError "Could not resolve {repr term}"
else if arg.getKind == ``simpStar then
starArg := true
simpStuff := simpStuff.push arg
else
throwUnsupportedSyntax
-- Build a mock simp call to build a simp context that corresponds to `simp [simpStuff]`
let stx `(tactic| simp +unfoldPartialApp [$(Syntax.TSepArray.ofElems simpStuff),*])
-- logInfo s!"{stx}"
let res mkSimpContext stx.raw
(eraseLocal := false)
(simpTheorems := getSpecSimpTheorems)
(ignoreStarArg := ignoreStarArg)
-- logInfo m!"{res.ctx.simpTheorems.map (·.toUnfold.toList)}"
if starArg && !ignoreStarArg then
let fvars getPropHyps
for fvar in fvars do
unless specThms.isErased (.local fvar) do
try
let thm mkSpecTheoremFromLocal fvar
specThms := addSpecTheoremEntry specThms thm
catch _ => continue
return { config, specThms, simpCtx := res.ctx, simprocs := res.simprocs }
def isDuplicable (e : Expr) : Bool := match e with
| .bvar .. => true
| .mvar .. => true
| .fvar .. => true
| .const .. => true
| .lit .. => true
| .sort .. => true
| .mdata _ e => isDuplicable e
| .proj _ _ e => isDuplicable e
| .lam .. => false
| .forallE .. => false
| .letE .. => false
| .app .. => e.isAppOf ``OfNat.ofNat
def withSharing (name : Name) (type : Expr) (val : Expr) (k : Expr (Expr VCGenM Expr) VCGenM α) (kind : LocalDeclKind := .default) : VCGenM α :=
if isDuplicable val then
k val pure
else
withLetDecl name type val (kind := kind) fun fv => do
k fv (liftM <| mkForallFVars #[fv] ·)
/-- Reduces (1) Prod projection functions and (2) Projs in application heads,
and (3) beta reduces. -/
private partial def reduceProjBeta? (e : Expr) : MetaM (Option Expr) :=
go none e.getAppFn e.getAppRevArgs
where
go lastReduction f rargs := do
match f with
| .mdata _ f => go lastReduction f rargs
| .app f a => go lastReduction f (rargs.push a)
| .lam .. =>
if rargs.size = 0 then return lastReduction
let e' := f.betaRev rargs
go (some e') e'.getAppFn e'.getAppRevArgs
| .const name .. =>
let env getEnv
match env.getProjectionStructureName? name with
| some ``Prod => -- only reduce fst and snd for now
match Meta.unfoldDefinition? (mkAppRev f rargs) with
| some e' => go lastReduction e'.getAppFn e'.getAppRevArgs
| none => pure lastReduction
| _ => pure lastReduction
| .proj .. => match reduceProj? f with
| some f' =>
let e' := mkAppRev f' rargs
go (some e') e'.getAppFn e'.getAppRevArgs
| none => pure lastReduction
| _ => pure lastReduction
partial def step (ctx : Context) (fuel : Fuel) (goal : MGoal) (name : Name) : MetaM (Expr × Array MVarId) := do
withReducible do
let (res, state) StateRefT'.run (ReaderT.run (onGoal goal name) ctx) { fuel }
return (res, state.vcs)
where
onFail (goal : MGoal) (name : Name) : VCGenM Expr := do
-- trace[Elab.Tactic.Do.vcgen] "fail {goal.toExpr}"
-- logInfo m!"fail {goal.toExpr}"
emitVC goal.toExpr name
tryGoal (goal : Expr) (name : Name) : VCGenM Expr := do
-- trace[Elab.Tactic.Do.vcgen] "tryGoal: {goal}"
forallTelescope goal fun xs body => do
let res try mStart body catch _ =>
-- trace[Elab.Tactic.Do.vcgen] "not an MGoal: {body}"
return mkLambdaFVars xs ( emitVC body name)
-- trace[Elab.Tactic.Do.vcgen] "an MGoal: {res.goal.toExpr}"
let mut prf onGoal res.goal name
-- logInfo m!"tryGoal: {res.goal.toExpr}"
-- res.goal.checkProof prf
if let some proof := res.proof? then
prf := mkApp proof prf
@@ -68,7 +241,7 @@ where
for mvar in mvars do
if mvar.isAssigned then continue
mvar.withContext <| do
-- trace[Elab.Tactic.Do.vcgen] "assignMVars {← mvar.getTag}, isDelayedAssigned: {← mvar.isDelayedAssigned},\n{mvar}"
-- trace[Elab.Tactic.Do.vcgen] "assignMVars {← mvar.getTag}, isDelayedAssigned: {← mvar.isDelayedAssigned}, type: {← mvar.getType}"
let ty mvar.getType
if ( isProp ty) || ty.isAppOf ``PostCond || ty.isAppOf ``SPred then
-- This code path will re-introduce `mvar` as a synthetic opaque goal upon discharge failure.
@@ -83,280 +256,162 @@ where
onGoal goal name : VCGenM Expr := do
let T := goal.target
let T := ( reduceProjBeta? T).getD T -- very slight simplification
-- trace[Elab.Tactic.Do.vcgen] "target: {T}"
-- logInfo m!"target: {T}"
let goal := { goal with target := T }
let f := T.getAppFn
if let .lam binderName .. := f then
burnOne
return mIntroForall goal mkIdent ( mkFreshUserName binderName) (fun g => onGoal g name)
if f.isLet || f.isConstOf ``SPred.imp then
burnOne
return mIntro goal ( `(binderIdent| _)) (fun g => onGoal g name)
if f.isConstOf ``PredTrans.apply then
if f.isLambda then
return onLambda goal name
if f.isConstOf ``SPred.imp then
return onImp goal name
else if f.isConstOf ``PredTrans.apply then
return onWPApp goal name
onFail { goal with target := T } name
onImp goal name : VCGenM Expr := ifOutOfFuel (onFail goal name) do
burnOne
(·.2) <$> mIntro goal ( `(binderIdent| _)) (fun g =>
do return ((), onGoal g name))
onLambda goal name : VCGenM Expr := ifOutOfFuel (onFail goal name) do
burnOne
(·.2) <$> mIntroForall goal ( `(binderIdent| _)) (fun g =>
do return ((), onGoal g name))
onWPApp goal name : VCGenM Expr := ifOutOfFuel (onFail goal name) do
let args := goal.target.getAppArgs
let trans := args[2]!
-- logInfo m!"trans: {trans}"
let Q := args[3]!
let wp instantiateMVarsIfMVarApp trans
let_expr c@WP.wp m ps instWP α e := wp | onFail goal name
-- NB: e here is a monadic expression, in the "object language"
let e instantiateMVarsIfMVarApp e
let e := e.headBeta
let goal := goal.withNewProg e -- to persist the instantiation of `e` and `trans`
trace[Elab.Tactic.Do.vcgen] "Program: {e}"
match_expr wp with
| c@WP.wp m ps instWP α e =>
let e instantiateMVarsIfMVarApp e
let e := e.headBeta
let [u, _] := c.constLevels! | panic! "PredTrans.apply has wrong number of levels"
trace[Elab.Tactic.Do.vcgen] "Target: {e}"
let goalWithNewProg e' :=
let wp' := mkApp5 c m ps instWP α e'
let args' := args.set! 2 wp'
{ goal with target := mkAppN (mkConst ``PredTrans.apply [u]) args' }
-- let-expressions
if let .letE x ty val body _nonDep := e.getAppFn' then
burnOne
return withLetDeclShared ( mkFreshUserName x) ty val fun shared fv leave => do
let e' := (body.instantiate1 fv).betaRev e.getAppRevArgs
let info? getSplitInfo? e'
if shared && isJP x && ctx.config.jp && info?.isSome then
leave ( onJoinPoint fv val (goal.withNewProg e') info?.get! name)
else
leave ( onWPApp (goal.withNewProg e') name)
-- if, dite and match-expressions (without `+jp` which is handled by `onJoinPoint`)
if let .some info getSplitInfo? e then
return onSplit goal info name
-- zeta-unfold local bindings (TODO don't do this unconditionally)
let f := e.getAppFn'
if let some (some val) f.fvarId?.mapM (·.getValue?) then
burnOne
trace[Elab.Tactic.Do.vcgen] "Call site of {f}"
if let some info knownJP? f.fvarId! then
return onJumpSite (goal.withNewProg e) info
else
-- lambda-expressions
if e.getAppFn'.isLambda && false then
-- We are likely in the implementation of a StateT function; do `mintro ∀s`
return onLambda goal name
-- let-expressions
if let .letE x ty val body _nonDep := e.getAppFn' then
burnOne
return withSharing x ty val fun fv leave => do
let e' := ((body.instantiate1 fv).betaRev e.getAppRevArgs)
leave ( onWPApp (goalWithNewProg e') name)
-- match-expressions
if let .some info := isMatcherAppCore? ( getEnv) e then
-- Bring into simp NF
let res? Simp.simpMatchDiscrs? info e
let e -- returns/continues only if old e is defeq to new e
if let .some res := res? then
burnOne
if let .some heq := res.proof? then
let prf onWPApp (goalWithNewProg res.expr) name
let prf := mkApp10 (mkConst ``Triple.rewrite_program c.constLevels!) m ps α goal.hyps Q instWP e res.expr heq prf
return prf
else
pure res.expr
else
pure e
-- Try reduce the matcher
let e match ( reduceMatcher? e) with
| .reduced e' =>
burnOne
return onWPApp (goalWithNewProg e') name
| .stuck _ => pure e
| _ => pure e
-- Last resort: Split match
-- logInfo m!"split match {e}"
burnOne
let mvar mkFreshExprSyntheticOpaqueMVar goal.toExpr (tag := name)
let mvars Split.splitMatch mvar.mvarId! e
assignMVars mvars
return mvar
-- Unfold local bindings (TODO don't do this unconditionally)
let f := e.getAppFn'
if let some (some val) f.fvarId?.mapM (·.getValue?) then
burnOne
let e' := val.betaRev e.getAppRevArgs
return onWPApp (goal.withNewProg e') name
-- delta-unfold definitions according to reducibility and spec attributes,
-- apply specifications
if f.isConst then
burnOne
-- Now try looking up and applying a spec
let (prf, specHoles) try
let specThm findSpec ctx.specThms wp
trace[Elab.Tactic.Do.vcgen] "Candidate spec for {f.constName!}: {specThm.proof}"
withDefault <| collectFreshMVars <| mSpec goal (fun _wp => return specThm) name
catch ex =>
trace[Elab.Tactic.Do.vcgen] "Failed to find spec for {wp}. Trying simp. Reason: {ex.toMessageData}"
-- logInfo m!"unfold local var {f}, new WP: {wpe}"
return onWPApp (goalWithNewProg e') name
-- Unfold definitions according to reducibility and spec attributes,
-- apply specifications
if f.isConst then
burnOne
-- First try to split Ifs. Just like for match splitting
if f.isConstOf ``ite || f.isConstOf ``dite then
-- Just like for match splitting above
let mvar mkFreshExprSyntheticOpaqueMVar goal.toExpr (tag := name)
let some (pos, neg) splitIfTarget? mvar.mvarId!
| liftMetaM <| throwError "Failed to split if {e}"
assignMVars [pos.mvarId, neg.mvarId]
return mvar
-- Now try looking up and applying a spec
try
let specThm findSpec ctx.specThms wp
trace[Elab.Tactic.Do.vcgen] "Candidate spec for {f.constName!}: {specThm.proof}"
let (prf, specHoles) withDefault <| collectFreshMVars <|
mSpec goal (fun _wp => return specThm) name
assignMVars specHoles.toList
return prf
catch ex =>
trace[Elab.Tactic.Do.vcgen] "Failed to find spec. Trying simp. Reason: {ex.toMessageData}"
-- Last resort: Simp and try again
let res Simp.simp e
unless res.expr != e do return onFail goal name
burnOne
if let .some heq := res.proof? then
trace[Elab.Tactic.Do.vcgen] "Simplified"
let prf onWPApp (goal.withNewProg res.expr) name
let prf onWPApp (goalWithNewProg res.expr) name
let prf := mkApp10 (mkConst ``Triple.rewrite_program c.constLevels!) m ps α goal.hyps Q instWP e res.expr heq prf
return prf
else
return onWPApp (goal.withNewProg res.expr) name
assignMVars specHoles.toList
return prf
return onFail goal name
return onWPApp (goalWithNewProg res.expr) name
return onFail goal name
| _ => return onFail goal name
-- Pre: It is `wp⟦e⟧ Q .. := goal.target` and `let .some info ← getSplitInfo? e`, without needing
-- to instantiate any MVars.
onSplit (goal : MGoal) (info : SplitInfo) (name : Name)
(withAltCtx : Nat Array Expr VCGenM Expr VCGenM Expr := fun _ _ k => k) : VCGenM Expr := do
let args := goal.target.getAppArgs
let Q := args[3]!
let_expr c@WP.wp m ps instWP α e := args[2]! | throwError "Expected wp⟦e⟧ Q in goal.target, got {goal.target}"
-- Bring into simp NF
let e -- returns/continues only if old e is defeq to new e
if let .some res info.simpDiscrs? e then
burnOne
if let .some heq := res.proof? then
let prf onWPApp (goal.withNewProg res.expr) name
let prf := mkApp10 (mkConst ``Triple.rewrite_program c.constLevels!) m ps α goal.hyps Q instWP e res.expr heq prf
return prf
else
pure res.expr
else
pure e
-- Try reduce the matcher
let e match ( reduceMatcher? e) with
| .reduced e' =>
burnOne
return onWPApp (goal.withNewProg e') name
| .stuck _ => pure e
| _ => pure e
-- throwError "Here we are {args}"
-- Last resort: Split match
trace[Elab.Tactic.Do.vcgen] "split match: {e}"
burnOne
-- context = fun e => H ⊢ₛ wp⟦e⟧ Q
let context withLocalDecl `e .default (mkApp m α) fun e => do
mkLambdaFVars #[e] (goal.withNewProg e).toExpr
return info.splitWithConstantMotive goal.toExpr (useSplitter := true) fun altSuff idx params => do
let res liftMetaM <| rwIfOrMatcher idx e
let goal' := goal.withNewProg res.expr
let prf withAltCtx idx params <| onWPApp goal' (name ++ altSuff)
let res Simp.mkCongrArg context res
res.mkEqMPR prf
def genVCs (goal : MVarId) (ctx : Context) (fuel : Fuel) : TacticM (Array MVarId) := do
let goal if ctx.config.noLetElim then pure goal else elimLets goal
let (mvar, goal) mStartMVar goal
mvar.withContext do
let (prf, vcs) step ctx (fuel := fuel) goal ( mvar.getTag)
mvar.assign prf
return vcs
-- Pre: We had `let x = zetadVal; e`, scoped through `x` as `fv` and have `goal.target = wp⟦e⟧ Q`,
-- where `e` is a splitter with `SplitInfo` `info`.
onJoinPoint (fv : Expr) (zetadVal : Expr) (goal : MGoal) (info : SplitInfo) (name : Name) : VCGenM Expr := do
burnOne
let args := goal.target.getAppArgs
let_expr c@WP.wp m ps instWP α e := args[2]! | throwError "Expected wp⟦e⟧ Q in goal.target, got {goal.target}"
trace[Elab.Tactic.Do.vcgen] "Join point {fv} with matcher {e.getAppFn}"
let .some resTy := info.resTy | throwError "Encountered dependent motive of {e} despite there being a join point."
let [uWP, _] := c.constLevels! | throwError "PredTrans.apply has wrong number of levels"
let σs := mkApp (mkConst ``PostShape.args [uWP]) ps
let joinTy inferType fv
let numJoinParams getNumJoinParams joinTy resTy
@[builtin_tactic Lean.Parser.Tactic.mvcgenStep]
def elabMVCGenStep : Tactic := fun stx => withMainContext do
let ctx mkSpecContext stx[1] stx[3]
let n := if stx[2].isNone then 1 else stx[2][0].toNat
let vcs genVCs ( getMainGoal) ctx (fuel := .limited n)
replaceMainGoal vcs.toList
let hypsTys forallBoundedTelescope joinTy numJoinParams fun joinArgs _body => do
let mut hypsTys := #[]
for (numParams, alt) in info.altInfos do
-- When the joinTy looks like `(x1 : α1) → ... → (xN : αN) → resTy`,
-- and the alt looks like `fun (p1 : β1) (pM : βM) => e[p1, ..., pM] : resTy)`,
-- this will produce type
-- `(x1 : α1) → ... → (xN : αN) → (p1 : β1) → ... → (pM : βM) → Prop`.
-- For `dite` and `jp : Nat → Unit → Id Nat`, this will be
-- `(x : Nat) → (y : Unit) → (h : condTy) → Prop` and
-- and
-- `(x : Nat) → (y : Unit) → (h : ¬condTy) → Prop`
-- For `ite` and `jp : Nat → Unit → Id Nat`, this will be
-- `(x : Nat) → (y : Unit) → Prop` and
-- and
-- `(x : Nat) → (y : Unit) → Prop`
-- For `match d with | some z => ... | none => ...` and `jp : Nat → Unit → Id Nat`, this will be
-- `(x : Nat) → (y : Unit) → (z : Nat) → Prop` and
-- and
-- `(x : Nat) → (y : Unit) → (z : Unit) → Prop`
hypsTys := hypsTys.push <| lambdaBoundedTelescope alt numParams fun altParams _body =>
mkForallFVars (joinArgs ++ altParams) (mkSort .zero)
return hypsTys
let hypsMVars hypsTys.mapIdxM fun i hypsTy =>
mkFreshExprSyntheticOpaqueMVar hypsTy (name.appendIndexAfter i)
let (joinPrf, joinGoal) forallBoundedTelescope joinTy numJoinParams fun joinParams _body => do
let φ info.splitWithConstantMotive (mkSort .zero) fun _suff idx altParams =>
return mkAppN hypsMVars[idx]! (joinParams ++ altParams)
withLocalDecl ( mkFreshUserName `h) .default φ fun h => do
-- NB: `mkJoinGoal` is not quite `goal.withNewProg` because we only take 4 args and clear
-- the stateful hypothesis of the goal.
let mkJoinGoal (e : Expr) :=
let wp := mkApp5 c m ps instWP α e
let args := args.set! 2 wp |>.take 4
let target := mkAppN (mkConst ``PredTrans.apply [uWP]) args
{ u := uWP, σs, hyps := emptyHyp uWP σs, target : MGoal }
let joinPrf mkLambdaFVars (joinParams.push h) ( onWPApp (mkJoinGoal (mkAppN fv joinParams)) name)
let joinGoal mkForallFVars (joinParams.push h) (mkJoinGoal (zetadVal.beta joinParams)).toExpr
-- `joinPrf : joinGoal` by zeta
return (joinPrf, joinGoal)
withLetDecl ( mkFreshUserName `joinPrf) joinGoal joinPrf (kind := .implDetail) fun joinPrf => do
let prf onSplit goal info name fun idx params doGoal => do
let altLCtxIdx := ( getLCtx).numIndices
let info : JumpSiteInfo := {
numJoinParams,
altParams := params,
altIdx := idx,
altLCtxIdx,
hyps := hypsMVars[idx]!.mvarId!,
joinPrf,
}
withJP fv.fvarId! info doGoal
mkLetFVars #[joinPrf] prf
onJumpSite (goal : MGoal) (info : JumpSiteInfo) : VCGenM Expr := do
let args := goal.target.getAppArgs
let_expr c@WP.wp _m ps _instWP _α e := args[2]! | throwError "Expected wp⟦e⟧ Q in goal.target, got {goal.target}"
let [uWP, _] := c.constLevels! | throwError "PredTrans.apply has wrong number of levels"
let σs := mkApp (mkConst ``PostShape.args [uWP]) ps
-- Try to frame as many hypotheses as possible into the local context so that they end up
-- in the shared precondition of the join point spec.
return mTryFrame goal fun goal => do
-- We need to revert excess state args (any expression `s` in `H[s] ⊢ₛ wp⟦jp x y z⟧ Q s`)
-- so that goal.hyps has type `Assertion (PredShape.args ps)` and we can use
-- `joinPrf (h : φ') : ⊢ₛ wp⟦jp a b c⟧ Q` to construct a proof.
-- Note that we discard `goal.hyps` anyway, so users won't observe anything.
return mRevertForallN goal (args.size - 4) ( mkFreshUserName `_) fun goal => do
let joinArgs := e.getAppArgs
let newLocalDecls := ( getLCtx).decls.foldl (init := #[]) (start := info.altLCtxIdx) Array.push
|>.filterMap id
|>.filter (not ·.isImplementationDetail)
let newLocals := newLocalDecls.map LocalDecl.toExpr
let altParams := info.altParams
trace[Elab.Tactic.Do.vcgen] "altParams: {altParams}, newLocals: {newLocals}"
let (φ, prf) forallBoundedTelescope ( info.hyps.getType) info.numJoinParams fun joinParams _prop => do
trace[Elab.Tactic.Do.vcgen] "joinParams: {joinParams}, actual joinParams: {e.getAppArgs}"
let eqs liftMetaM <| joinParams.zipWithM mkEq joinArgs
let φ := mkAndN eqs.toList
let prf mkAndIntroN ( liftMetaM <| joinArgs.mapM mkEqRefl).toList
let φ := φ.abstract newLocals
-- Invariant: `prf : (fun joinParams => φ) joinArgs`
let (_, φ, prf) newLocalDecls.foldrM (init := (newLocals, φ, prf)) fun decl (locals, φ, prf) => do
let locals := locals.pop
match decl.value? with
| some v =>
let type := decl.type.abstract locals
let val := v.abstract locals
let φ := mkLet decl.userName type val φ (nondep := decl.isNondep)
return (locals, φ, prf)
| none =>
let type := decl.type.abstract locals
let u getLevel type
let ψ := mkLambda decl.userName decl.binderInfo type φ
let φ := mkApp2 (mkConst ``Exists [u]) type ψ
let prf := mkApp4 (mkConst ``Exists.intro [u]) type ψ decl.toExpr prf
return (locals, φ, prf)
-- Abstract φ over the altParams in order to instantiate info.hyps below
let φ
if altParams == #[mkConst ``Unit.unit] then -- See `Match.forallAltVarsTelescope`
pure <| mkLambda `_ .default (mkConst ``Unit) φ
else
mkLambdaFVars altParams φ
return ( mkLambdaFVars joinParams φ, mkLambdaFVars joinParams prf)
info.hyps.assign φ
let φ := φ.beta (joinArgs ++ altParams)
let prf := prf.beta joinArgs
let jumpPrf := mkAppN info.joinPrf joinArgs
let jumpGoal inferType jumpPrf
let .forallE _ φ' .. := jumpGoal | throwError "jumpGoal {jumpGoal} is not a forall"
trace[Elab.Tactic.Do.vcgen] "φ applied: {φ}, prf applied: {prf}, type: {← inferType prf}"
let rwPrf rwIfOrMatcher info.altIdx φ'
trace[Elab.Tactic.Do.vcgen] "joinPrf: {← inferType info.joinPrf}"
let jumpPrf := mkAppN info.joinPrf (joinArgs.push ( rwPrf.mkEqMPR prf))
let prf₁ := mkApp2 (mkConst ``SPred.true_intro [uWP]) σs goal.hyps
let prf mkAppM ``SPred.entails.trans #[prf₁, jumpPrf]
-- goal.checkProof prf
return prf
end VCGen
@[builtin_tactic Lean.Parser.Tactic.mvcgenNoTrivial]
def elabMVCGenNoTrivial : Tactic := fun stx => withMainContext do
let ctx mkSpecContext stx[0] stx[1]
let vcs genVCs ( getMainGoal) ctx (fuel := .unlimited)
replaceMainGoal vcs.toList
@[builtin_tactic Lean.Parser.Tactic.mvcgen]
def elabMVCGen : Tactic := fun stx => withMainContext do
if mvcgen.warning.get ( getOptions) then
logWarningAt stx "The `mvcgen` tactic is experimental and still under development. Avoid using it in production projects."
-- I would like to define this simply as a macro
-- `(tactic| mvcgen_no_trivial $c $lemmas <;> try (guard_target =~ (⌜True⌝ ⊢ₛ _); mpure_intro; trivial))
-- but optConfig is not a leading_parser, and neither is the syntax for `lemmas`
let ctx mkSpecContext stx[1] stx[2]
let fuel := match ctx.config.stepLimit with
| some n => .limited n
| none => .unlimited
let goal getMainGoal
let goal if ctx.config.elimLets then elimLets goal else pure goal
let vcs VCGen.genVCs goal ctx fuel
let runOnVCs (tac : TSyntax `tactic) (vcs : Array MVarId) : TermElabM (Array MVarId) :=
vcs.flatMapM fun vc => List.toArray <$> Term.withSynthesize do
Tactic.run vc (Tactic.evalTactic tac *> Tactic.pruneSolvedGoals)
let vcs Term.TermElabM.run' do
let vcs if ctx.config.trivial then runOnVCs ( `(tactic| try mvcgen_trivial)) vcs else pure vcs
let vcs if ctx.config.leave then runOnVCs ( `(tactic| try mleave)) vcs else pure vcs
return vcs
-- Eliminating lets here causes some metavariables in `mkFreshPair_triple` to become nonassignable
-- so we don't do it. Presumably some weird delayed assignment thing is going on.
-- let vcs ← if ctx.config.elimLets then liftMetaM <| vcs.mapM elimLets else pure vcs
replaceMainGoal vcs.toList
let vcs genVCs ( getMainGoal) ctx (fuel := .unlimited)
let tac `(tactic| (try (try apply $(mkIdent ``Std.Do.SPred.Tactic.Pure.intro)); trivial))
let mut s := {}
let mut newVCs := #[]
for vc in vcs do
let (vcs, s') runTactic vc tac (s := s)
s := s'
newVCs := newVCs ++ vcs
replaceMainGoal newVCs.toList

View File

@@ -1,242 +0,0 @@
/-
Copyright (c) 2025 Lean FRO LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Graf
-/
module
prelude
public import Lean.Elab.Tactic.Simp
public import Lean.Elab.Tactic.Do.Attr
public section
namespace Lean.Elab.Tactic.Do
open Lean Parser Elab Tactic Meta Do SpecAttr
builtin_initialize registerTraceClass `Elab.Tactic.Do.vcgen
register_builtin_option mvcgen.warning : Bool := {
defValue := true
group := "debug"
descr := "disable `mvcgen` usage warning"
}
inductive Fuel where
| limited (n : Nat)
| unlimited
deriving DecidableEq
declare_config_elab elabConfig VCGen.Config
structure JumpSiteInfo where
/-- Number of join point arguments. -/
numJoinParams : Nat
/-- Index of the match alternative. -/
altIdx : Nat
/-- Parameter FVars of the match alternative. -/
altParams : Array Expr
/--
The size of the local context in the alternative after the match has been split and all splitter
parameters have been introduced.
This is so that we can construct the `Σ Lᵢ` part of the `hyps` field.
-/
altLCtxIdx : Nat
/--
MVar to be filled with the stateful hypotheses of the match arm. This should include
bindings from the local context `Lᵢ` of the call site and is of the form (`x,y,z ∈ Lᵢ`)
`Σ Lᵢ, ⌜x = a ∧ y = b ∧ z = c⌝ ∧ Hᵢ`, where `..., Lᵢ ⊢ Hᵢ ⊢ₛ wp[jp x y z] Q` is the call site.
The `Σ Lᵢ` is short for something like
`let x := ...; ∃ y (h : y = ...), ∃ z, ∃ (h₂ : p)`.
-/
hyps : MVarId
/--
The proof that jump sites should use to discharge `Hᵢ ⊢ₛ wp[jp a b c] Q`.
-/
joinPrf : Expr
structure Context where
config : VCGen.Config
specThms : SpecTheorems
simpCtx : Simp.Context
simprocs : Simp.SimprocsArray
jps : FVarIdMap JumpSiteInfo := {}
structure State where
fuel : Fuel := .unlimited
simpState : Simp.State := {}
/--
The verification conditions that have been generated so far.
Includes `Type`-valued goals arising from instantiation of specifications.
-/
vcs : Array MVarId := #[]
abbrev VCGenM := ReaderT Context (StateRefT State MetaM)
def burnOne : VCGenM PUnit := do
let s get
match s.fuel with
| Fuel.limited 0 => return ()
| Fuel.limited (n + 1) => set { s with fuel := .limited n }
| Fuel.unlimited => return ()
def ifOutOfFuel (x : VCGenM α) (k : VCGenM α) : VCGenM α := do
let s get
match s.fuel with
| Fuel.limited 0 => x
| _ => k
def emitVC (subGoal : Expr) (name : Name) : VCGenM Expr := do
let m liftM <| mkFreshExprSyntheticOpaqueMVar subGoal (tag := name)
modify fun s => { s with vcs := s.vcs.push m.mvarId! }
return m
def addSubGoalAsVC (goal : MVarId) : VCGenM PUnit := do
modify fun s => { s with vcs := s.vcs.push goal }
def liftSimpM (x : SimpM α) : VCGenM α := do
let ctx read
let s get
let mref := (Simp.mkDefaultMethodsCore ctx.simprocs).toMethodsRef
let (a, simpState) x mref ctx.simpCtx |>.run s.simpState
set { s with simpState }
return a
instance : MonadLift SimpM VCGenM where
monadLift x := liftSimpM x
def withJP (jp : FVarId) (info : JumpSiteInfo) : VCGenM α VCGenM α :=
ReaderT.adapt fun ctx => { ctx with jps := ctx.jps.insert jp info }
def knownJP? (jp : FVarId) : VCGenM (Option JumpSiteInfo) := do
return ( read).jps.get? jp
def isDuplicable (e : Expr) : Bool := match e with
| .bvar .. => true
| .mvar .. => true
| .fvar .. => true
| .const .. => true
| .lit .. => true
| .sort .. => true
| .mdata _ e => isDuplicable e
| .proj _ _ e => isDuplicable e
| .lam .. => false
| .forallE .. => false
| .letE .. => false
| .app .. => e.isAppOf ``OfNat.ofNat
def withLetDeclShared (name : Name) (type : Expr) (val : Expr) (k : Bool Expr (Expr VCGenM Expr) VCGenM α) (kind : LocalDeclKind := .default) : VCGenM α :=
if isDuplicable val then
k false val pure
else
withLetDecl name type val (kind := kind) fun fv => do
k true fv (liftM <| mkLetFVars #[fv] ·)
/-- TODO: Fix this when rewriting the do elaborator -/
def isJP (n : Name) : Bool := n.eraseMacroScopes == `__do_jp
partial def getNumJoinParams (joinTy : Expr) (resTy : Expr) : MetaM Nat := do
if joinTy.isMData then
return getNumJoinParams joinTy.consumeMData resTy
if joinTy == resTy then
return 0
else if joinTy.isForall then
return 1 + ( getNumJoinParams joinTy.consumeMData.bindingBody! resTy)
else
throwError "getNumJoinParams: residual joinTy not a forall: {joinTy}"
/-- Reduces (1) Prod projection functions and (2) Projs in application heads,
and (3) beta reduces. Will not unfold projection functions unless further beta reduction happens. -/
partial def reduceProjBeta? (e : Expr) : MetaM (Option Expr) :=
go none e.getAppFn e.getAppRevArgs
where
go lastReduction f rargs := do
match f with
| .mdata _ f => go lastReduction f rargs
| .app f a => go lastReduction f (rargs.push a)
| .lam .. =>
if rargs.size = 0 then return lastReduction
let e' := f.betaRev rargs
go (some e') e'.getAppFn e'.getAppRevArgs
| .const name .. =>
let env getEnv
match env.getProjectionStructureName? name with
| some ``Prod => -- only reduce fst and snd for now
match Meta.unfoldDefinition? (mkAppRev f rargs) with
| some e' => go lastReduction e'.getAppFn e'.getAppRevArgs
| none => pure lastReduction
| _ => pure lastReduction
| .proj .. => match reduceProj? f with
| some f' =>
let e' := mkAppRev f' rargs
go (some e') e'.getAppFn e'.getAppRevArgs
| none => pure lastReduction
| _ => pure lastReduction
def mkSpecContext (optConfig : Syntax) (lemmas : Syntax) (ignoreStarArg := false) : TacticM Context := do
let config elabConfig optConfig
let mut specThms getSpecTheorems
let mut simpStuff := #[]
let mut starArg := false
for arg in lemmas[1].getSepArgs do
if arg.getKind == ``simpErase then
try
-- Try and build SpecTheorems for the lemma to erase to see if it's
-- meant to be interpreted by SpecTheorems. Otherwise fall back to SimpTheorems.
let specThm
if let some fvar Term.isLocalIdent? arg[1] then
mkSpecTheoremFromLocal fvar.fvarId!
else
let id := arg[1]
if let .ok declName observing (realizeGlobalConstNoOverloadWithInfo id) then
mkSpecTheoremFromConst declName
else
withRef id <| throwUnknownConstant id.getId.eraseMacroScopes
specThms := specThms.eraseCore specThm.proof
catch _ =>
simpStuff := simpStuff.push arg -- simp tracks its own erase stuff
else if arg.getKind == ``simpLemma then
unless arg[0].isNone && arg[1].isNone do
-- When there is ←, →, ↑ or ↓ then this is for simp
simpStuff := simpStuff.push arg
continue
let term := arg[2]
match Term.resolveId? term (withInfo := true) <|> Term.elabCDotFunctionAlias? term with
| some (.const declName _) =>
let info getConstInfo declName
try
let thm mkSpecTheoremFromConst declName
specThms := addSpecTheoremEntry specThms thm
catch _ =>
simpStuff := simpStuff.push arg
| some (.fvar fvar) =>
let decl getFVarLocalDecl (.fvar fvar)
try
let thm mkSpecTheoremFromLocal fvar
specThms := addSpecTheoremEntry specThms thm
catch _ =>
simpStuff := simpStuff.push arg
| _ => withRef term <| throwError "Could not resolve {repr term}"
else if arg.getKind == ``simpStar then
starArg := true
simpStuff := simpStuff.push arg
else
throwUnsupportedSyntax
-- Build a mock simp call to build a simp context that corresponds to `simp [simpStuff]`
let stx `(tactic| simp +unfoldPartialApp -zeta [$(Syntax.TSepArray.ofElems simpStuff),*])
-- logInfo s!"{stx}"
let res mkSimpContext stx.raw
(eraseLocal := false)
(simpTheorems := getSpecSimpTheorems)
(ignoreStarArg := ignoreStarArg)
-- logInfo m!"{res.ctx.simpTheorems.map (·.toUnfold.toList)}"
if starArg && !ignoreStarArg then
let fvars getPropHyps
for fvar in fvars do
unless specThms.isErased (.local fvar) do
try
let thm mkSpecTheoremFromLocal fvar
specThms := addSpecTheoremEntry specThms thm
catch _ => continue
return { config, specThms, simpCtx := res.ctx, simprocs := res.simprocs }

View File

@@ -1,104 +0,0 @@
/-
Copyright (c) 2025 Lean FRO LLC. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Sebastian Graf
-/
module
prelude
public import Lean.Meta.Tactic.FunInd
public section
namespace Lean.Elab.Tactic.Do
open Lean Elab Tactic Meta
inductive SplitInfo where
| ite (e : Expr)
| dite (e : Expr)
| matcher (matcherApp : MatcherApp)
deriving Inhabited
namespace SplitInfo
def resTy (info : SplitInfo) : Option Expr := match info with
| ite e => e.getArg! 0
| dite e => e.getArg! 0
-- For a matcher, the motive has type `(discr1 : α) → ... → (discrN : α) → Type`.
-- We want to return `Type` component and fail if it depends on any of the discriminant values.
| matcher matcherApp => do
let motive := matcherApp.motive
let e : Expr Nat.repeat (n := matcherApp.discrInfos.size) (a := some motive) fun e =>
match e with
| some (.lam _ _ e _) => e
| _ => none
unless e.looseBVarRange = motive.looseBVarRange do none
return e
/--
A list of pairs `(numParams, alt)` per match alternative, where `numParams` is the
number of parameters of the alternative and `alt` is the alternative.
-/
def altInfos (info : SplitInfo) : Array (Nat × Expr) := match info with
| ite e => #[(0, e.getArg! 3), (1, e.getArg! 4)]
| dite e => #[(0, e.getArg! 3), (1, e.getArg! 4)]
| matcher matcherApp => matcherApp.altNumParams.mapIdx fun idx numParams =>
(numParams, matcherApp.alts[idx]!)
def splitWithConstantMotive
{n} [MonadLiftT MetaM n] [MonadControlT MetaM n] [Monad n] [MonadError n] [MonadEnv n] [MonadLog n]
[AddMessageContext n] [MonadOptions n]
(info : SplitInfo) (resTy : Expr) (onAlt : Name Nat Array Expr n Expr) (useSplitter := false) : n Expr := match info with
| ite e => do
let u getLevel resTy
let c := e.getArg! 1
let h := e.getArg! 2
if useSplitter then -- dite is the "splitter" for ite
let n liftMetaM <| mkFreshUserName `h
let t withLocalDecl n .default c fun h => do mkLambdaFVars #[h] ( onAlt `isTrue 0 #[])
let e withLocalDecl n .default (mkNot c) fun h => do mkLambdaFVars #[h] ( onAlt `isFalse 1 #[])
return mkApp5 (mkConst ``_root_.dite [u]) resTy c h t e
else
let t onAlt `isTrue 0 #[]
let e onAlt `isFalse 1 #[]
return mkApp5 (mkConst ``_root_.ite [u]) resTy c h t e
| dite e => do
let u getLevel resTy
let c := e.getArg! 1
let h := e.getArg! 2
let n liftMetaM <| mkFreshUserName `h
let t withLocalDecl n .default c fun h => do mkLambdaFVars #[h] ( onAlt `isTrue 0 #[h])
let e withLocalDecl n .default (mkNot c) fun h => do mkLambdaFVars #[h] ( onAlt `isFalse 1 #[h])
return mkApp5 (mkConst ``_root_.dite [u]) resTy c h t e
| matcher matcherApp => do
(·.toExpr) <$> matcherApp.transform
(useSplitter := useSplitter) (addEqualities := useSplitter) -- (freshenNames := true)
(onMotive := fun _xs _motive => pure resTy)
(onAlt := fun idx _ty params _alt => onAlt ((`h).appendIndexAfter (idx+1)) idx params)
def simpDiscrs? (info : SplitInfo) (e : Expr) : SimpM (Option Simp.Result) := match info with
| dite _ | ite _ => return none -- Tricky because we need to simultaneously rewrite `[Decidable c]`
| matcher matcherApp => Simp.simpMatchDiscrs? matcherApp.toMatcherInfo e
end SplitInfo
def getSplitInfo? (e : Expr) : MetaM (Option SplitInfo) := do
if e.isAppOf ``ite then
return some (SplitInfo.ite e)
if e.isAppOf ``dite then
return some (SplitInfo.dite e)
if let .some matcherApp matchMatcherApp? (alsoCasesOn := true) e then
return some (SplitInfo.matcher matcherApp)
else
return none
def rwIfOrMatcher (idx : Nat) (e : Expr) : MetaM Simp.Result := do
if e.isAppOf ``ite || e.isAppOf ``dite then
let c := e.getArg! 1
let c := if idx = 0 then c else mkNot c
let .some fv findLocalDeclWithType? c
| throwError "Failed to proof for if condition {c}"
FunInd.rwIfWith (mkFVar fv) e
else
FunInd.rwMatcher idx e

View File

@@ -71,8 +71,8 @@ def getEntries {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension
/-- Get the current state of the given `SimplePersistentEnvExtension`. -/
def getState {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ) (env : Environment)
(asyncMode := ext.toEnvExtension.asyncMode) (asyncDecl : Name := .anonymous) : σ :=
(PersistentEnvExtension.getState (asyncMode := asyncMode) (asyncDecl := asyncDecl) ext env).2
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
(PersistentEnvExtension.getState (asyncMode := asyncMode) ext env).2
/-- Set the current state of the given `SimplePersistentEnvExtension`. This change is *not* persisted across files. -/
def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (s : σ) : Environment :=
@@ -82,6 +82,11 @@ def setState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : En
def modifyState {α σ : Type} (ext : SimplePersistentEnvExtension α σ) (env : Environment) (f : σ σ) : Environment :=
PersistentEnvExtension.modifyState ext env (fun entries, s => (entries, f s))
@[inherit_doc PersistentEnvExtension.findStateAsync]
def findStateAsync {α σ : Type} [Inhabited σ] (ext : SimplePersistentEnvExtension α σ)
(env : Environment) (declPrefix : Name) : σ :=
PersistentEnvExtension.findStateAsync ext env declPrefix |>.2
end SimplePersistentEnvExtension
/-- Environment extension for tagging declarations.
@@ -106,13 +111,16 @@ instance : Inhabited TagDeclarationExtension :=
def tag (ext : TagDeclarationExtension) (env : Environment) (declName : Name) : Environment :=
have : Inhabited Environment := env
assert! env.getModuleIdxFor? declName |>.isNone -- See comment at `TagDeclarationExtension`
ext.addEntry (asyncDecl := declName) env declName
assert! env.asyncMayContain declName
ext.addEntry env declName
def isTagged (ext : TagDeclarationExtension) (env : Environment) (declName : Name)
(asyncMode := ext.toEnvExtension.asyncMode) : Bool :=
def isTagged (ext : TagDeclarationExtension) (env : Environment) (declName : Name) : Bool :=
match env.getModuleIdxFor? declName with
| some modIdx => (ext.getModuleEntries env modIdx).binSearchContains declName Name.quickLt
| none => (ext.getState (asyncMode := asyncMode) (asyncDecl := declName) env).contains declName
| none => if ext.toEnvExtension.asyncMode matches .async then
(ext.findStateAsync env declName).contains declName
else
(ext.getState env).contains declName
end TagDeclarationExtension
@@ -123,7 +131,6 @@ structure MapDeclarationExtension (α : Type) extends PersistentEnvExtension (Na
deriving Inhabited
def mkMapDeclarationExtension (name : Name := by exact decl_name%)
(asyncMode : EnvExtension.AsyncMode := .async .mainEnv)
(exportEntriesFn : Environment NameMap α OLeanLevel Array (Name × α) :=
fun _ s _ => s.toArray) :
IO (MapDeclarationExtension α) :=
@@ -133,7 +140,7 @@ def mkMapDeclarationExtension (name : Name := by exact decl_name%)
addImportedFn := fun _ => pure {}
addEntryFn := fun s (n, v) => s.insert n v
exportEntriesFnEx env s level := exportEntriesFn env s level
asyncMode
asyncMode := .async
replay? := some fun _ newState newConsts s =>
newConsts.foldl (init := s) fun s c =>
if let some a := newState.find? c then
@@ -146,20 +153,23 @@ namespace MapDeclarationExtension
def insert (ext : MapDeclarationExtension α) (env : Environment) (declName : Name) (val : α) : Environment :=
have : Inhabited Environment := env
assert! env.getModuleIdxFor? declName |>.isNone -- See comment at `MapDeclarationExtension`
ext.addEntry (asyncDecl := declName) env (declName, val)
if !env.asyncMayContain declName then
panic! s!"MapDeclarationExtension.insert: cannot insert {declName} into {ext.name}, it is not contained in {env.asyncPrefix?}"
else
ext.addEntry env (declName, val)
def find? [Inhabited α] (ext : MapDeclarationExtension α) (env : Environment) (declName : Name)
(asyncMode := ext.toEnvExtension.asyncMode) (level := OLeanLevel.exported) : Option α :=
(level := OLeanLevel.exported) : Option α :=
match env.getModuleIdxFor? declName with
| some modIdx =>
match (ext.getModuleEntries (level := level) env modIdx).binSearch (declName, default) (fun a b => Name.quickLt a.1 b.1) with
| some e => some e.2
| none => none
| none => (ext.getState (asyncMode := asyncMode) (asyncDecl := declName) env).find? declName
| none => (ext.findStateAsync env declName).find? declName
def contains [Inhabited α] (ext : MapDeclarationExtension α) (env : Environment) (declName : Name) : Bool :=
match env.getModuleIdxFor? declName with
| some modIdx => (ext.getModuleEntries env modIdx).binSearchContains (declName, default) (fun a b => Name.quickLt a.1 b.1)
| none => (ext.getState (asyncDecl := declName) env).contains declName
| none => (ext.findStateAsync env declName).contains declName
end MapDeclarationExtension

View File

@@ -354,7 +354,8 @@ def setDiagnostics (env : Environment) (diag : Diagnostics) : Environment :=
end Kernel.Environment
@[deprecated Kernel.Exception (since := "2024-12-12")]
abbrev KernelException := Kernel.Exception
inductive ConstantKind where
| defn | thm | «axiom» | «opaque» | quot | induct | ctor | recursor
@@ -433,19 +434,17 @@ private def AsyncContext.mayContain (ctx : AsyncContext) (n : Name) : Bool :=
Constant info and environment extension states eventually resulting from async elaboration.
-/
private structure AsyncConst where
constInfo : AsyncConstantInfo
constInfo : AsyncConstantInfo
/--
Reported extension state eventually fulfilled by promise; may be missing for tasks (e.g. kernel
checking) that can eagerly guarantee they will not report any state.
-/
exts? : Option (Task (Array EnvExtensionState))
exts? : Option (Task (Array EnvExtensionState))
/--
`Task AsyncConsts` except for problematic recursion. The set of nested constants created while
elaborating this constant.
-/
aconstsImpl : Task Dynamic
/-- True if generated by `realizeConst`. -/
isRealized : Bool := false
consts : Task Dynamic
/-- Data structure holding a sequence of `AsyncConst`s optimized for efficient access. -/
private structure AsyncConsts where
@@ -457,10 +456,6 @@ private structure AsyncConsts where
normalizedTrie : NameTrie AsyncConst
deriving Inhabited, TypeName
private def AsyncConst.aconsts (c : AsyncConst) : Task AsyncConsts :=
c.aconstsImpl.map (sync := true) fun dyn =>
dyn.get? AsyncConsts |>.getD default
private def AsyncConsts.add (aconsts : AsyncConsts) (aconst : AsyncConst) : AsyncConsts :=
let normalizedName := privateToUserName aconst.constInfo.name
if let some aconst' := aconsts.normalizedTrie.find? normalizedName then
@@ -493,7 +488,7 @@ private partial def AsyncConsts.findRec? (aconsts : AsyncConsts) (declName : Nam
-- If privacy is the only difference between `declName` and `findPrefix?` result, we can assume
-- `declName` does not exist according to the `add` invariant
guard <| privateToUserName c.constInfo.name != privateToUserName declName
let aconsts c.aconsts.get
let aconsts c.consts.get.get? AsyncConsts
AsyncConsts.findRec? aconsts declName
/-- Like `findRec?`; allocating tasks is (currently?) too costly to do always. -/
@@ -501,21 +496,10 @@ private partial def AsyncConsts.findRecTask (aconsts : AsyncConsts) (declName :
let some c := aconsts.findPrefix? declName | .pure none
if c.constInfo.name == declName then
return .pure c
c.aconsts.bind (sync := true) fun aconsts => Id.run do
c.consts.bind (sync := true) fun aconsts => Id.run do
let some aconsts := aconsts.get? AsyncConsts | .pure none
AsyncConsts.findRecTask aconsts declName
/-- Like `findRec?` but also returns the constant that has `declName` in its `consts`, if any. -/
private partial def AsyncConsts.findRecAndParent? (aconsts : AsyncConsts) (declName : Name) : Option (AsyncConst × Option AsyncConst) :=
go none aconsts
where go parent? aconsts := do
let c aconsts.findPrefix? declName
if c.constInfo.name == declName then
return (c, parent?)
-- If privacy is the only difference between `declName` and `findPrefix?` result, we can assume
-- `declName` does not exist according to the `add` invariant
guard <| privateToUserName c.constInfo.name != privateToUserName declName
go (some c) c.aconsts.get
/-- Accessibility levels of declarations in `Lean.Environment`. -/
private inductive Visibility where
/-- Information private to the module. -/
@@ -609,7 +593,7 @@ structure Environment where
/--
Task collecting all realizations from the current and already-forked environment branches, akin to
how `checked` collects all declarations. We only use it as a fallback in
`findAsyncCore?`/`getState`; see there.
`findAsyncCore?`/`findStateAsync`; see there.
-/
private allRealizations : Task (NameMap AsyncConst) := .pure {}
/--
@@ -690,6 +674,18 @@ def importEnv? (env : Environment) : Option Environment :=
def unlockAsync (env : Environment) : Environment :=
{ env with asyncCtx? := none }
/--
Checks whether the given declaration name may potentially added, or have been added, to the current
environment branch, which is the case either if this is the main branch or if the declaration name
is a suffix (modulo privacy and hygiene information) of the top-level declaration name for which
this branch was created.
This function should always be checked before modifying an `AsyncMode.async` environment extension
to ensure `findStateAsync` will be able to find the modification from other branches.
-/
def asyncMayContain (env : Environment) (declName : Name) : Bool :=
env.asyncCtx?.all (·.mayContain declName)
@[extern "lean_elab_add_decl"]
private opaque addDeclCheck (env : Environment) (maxHeartbeats : USize) (decl : @& Declaration)
(cancelTk? : @& Option IO.CancelToken) : Except Kernel.Exception Environment
@@ -724,14 +720,14 @@ def addDeclCore (env : Environment) (maxHeartbeats : USize) (decl : @& Declarati
env := { env with asyncConstsMap.private := env.asyncConstsMap.private.add {
constInfo := .ofConstantInfo info
exts? := none
aconstsImpl := .pure <| .mk (α := AsyncConsts) default
consts := .pure <| .mk (α := AsyncConsts) default
} }
-- TODO
if true /- !isPrivateName n-/ then
env := { env with asyncConstsMap.public := env.asyncConstsMap.public.add {
constInfo := .ofConstantInfo info
exts? := none
aconstsImpl := .pure <| .mk (α := AsyncConsts) default
consts := .pure <| .mk (α := AsyncConsts) default
} }
return env
@@ -753,7 +749,7 @@ private def lakeAdd (env : Environment) (cinfo : ConstantInfo) : Environment :=
asyncConstsMap := env.asyncConstsMap.map (·.add {
constInfo := .ofConstantInfo cinfo
exts? := none
aconstsImpl := .pure <| .mk (α := AsyncConsts) default
consts := .pure <| .mk (α := AsyncConsts) default
})
}
@@ -761,26 +757,22 @@ private def lakeAdd (env : Environment) (cinfo : ConstantInfo) : Environment :=
@[extern "lean_is_reserved_name"]
private opaque isReservedName (env : Environment) (name : Name) : Bool
@[inline] private def findAsyncConst? (env : Environment) (n : Name) (skipRealize := false) :
Option AsyncConst := do
if let some c := env.asyncConsts.find? n then
-- Constant for which an asynchronous elaboration task was spawned
-- (this is an optimized special case of the next branch)
return c
if let some c := env.asyncConsts.findRec? n then
-- Constant generated in a different environment branch
return c
if !skipRealize && isReservedName env n then
if let some c := env.allRealizations.get.find? n then
return c
-- Not in the kernel environment nor in the name prefix of a known environment branch: undefined
-- by `addDeclCore` invariant.
none
/-- `findAsync?` after `base` access -/
private def findAsyncCore? (env : Environment) (n : Name) (skipRealize := false) :
Option AsyncConstantInfo := do
env.findAsyncConst? n (skipRealize := skipRealize) |>.map (·.constInfo)
if let some c := env.asyncConsts.find? n then
-- Constant for which an asynchronous elaboration task was spawned
-- (this is an optimized special case of the next branch)
return c.constInfo
if let some c := env.asyncConsts.findRec? n then
-- Constant generated in a different environment branch
return c.constInfo
if !skipRealize && isReservedName env n then
if let some c := env.allRealizations.get.find? n then
return c.constInfo
-- Not in the kernel environment nor in the name prefix of a known environment branch: undefined
-- by `addDeclCore` invariant.
none
/-- Like `findAsyncCore?`; allocating tasks is (currently?) too costly to do always. -/
private def findTaskCore (env : Environment) (n : Name) (skipRealize := false) :
@@ -1040,7 +1032,7 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind)
| some v => v.exts
-- any value should work here, `base` does not block
| none => env.base.private.extensions)
aconstsImpl := constPromise.result?.map (sync := true) fun
consts := constPromise.result?.map (sync := true) fun
| some v => .mk v.nestedConsts.private
| none => .mk (α := AsyncConsts) default
}
@@ -1051,7 +1043,7 @@ def addConstAsync (env : Environment) (constName : Name) (kind : ConstantKind)
| some c => c.exportedConstInfo
| none => mkFallbackConstInfo constName exportedKind
}
aconstsImpl := constPromise.result?.map (sync := true) fun
consts := constPromise.result?.map (sync := true) fun
| some v => .mk v.nestedConsts.public
| none => .mk (α := AsyncConsts) default
}
@@ -1123,9 +1115,9 @@ def AddConstAsyncResult.commitConst (res : AddConstAsyncResult) (env : Environme
/--
Assuming `Lean.addDecl` has been run for the constant to be added on the async environment branch,
commits the full constant info from that call to the main environment, (asynchronously) waits for
the final kernel environment resulting from the `addDecl` call, and commits it to the main branch as
well, unblocking kernel additions there. All `commitConst` preconditions apply.
commits the full constant info from that call to the main environment, waits for the final kernel
environment resulting from the `addDecl` call, and commits it to the main branch as well, unblocking
kernel additions there. All `commitConst` preconditions apply.
-/
def AddConstAsyncResult.commitCheckEnv (res : AddConstAsyncResult) (env : Environment) :
IO Unit := do
@@ -1133,9 +1125,8 @@ def AddConstAsyncResult.commitCheckEnv (res : AddConstAsyncResult) (env : Enviro
-- `info?`
if !( res.constPromise.isResolved) then
res.commitConst env
BaseIO.chainTask (sync := true) env.checked fun checked => do
res.checkedEnvPromise.resolve checked
BaseIO.chainTask (sync := true) env.allRealizations res.allRealizationsPromise.resolve
res.checkedEnvPromise.resolve env.checked.get
res.allRealizationsPromise.resolve env.allRealizations.get
/--
Checks whether `findAsync?` would return a result.
@@ -1199,23 +1190,6 @@ def instantiateValueLevelParams! (c : ConstantInfo) (ls : List Level) : Expr :=
end ConstantInfo
/--
Branch specification for asynchronous environment extension access.
Note: For declarations not created via `addConstAsync`, including those created via `realizeConst`,
the two specifiers are equivalent.
-/
inductive AsyncBranch where
/--
The main branch that initiated adding a declaration, i.e. `AddConstAsyncResult.mainEnv`.
This is the more common case and true for e.g. all accesses from attributes.
-/
| mainEnv
/-- The async branch that finished adding a declaration, i.e. `AddConstAsyncResult.asyncEnv`. -/
| asyncEnv
deriving BEq
/--
Async access mode for environment extensions used in `EnvExtension.get/set/modifyState`.
When modified in concurrent contexts, extensions may need to switch to a different mode than the
@@ -1224,9 +1198,8 @@ registration time but can be overridden when calling the mentioned functions in
for specific accesses.
In all modes, the state stored into the `.olean` file for persistent environment extensions is the
result of `getState (asyncMode := .sync)` called on the main environment branch at the end of the
file, i.e. it encompasses all modifications on all branches except for `local` modifications for
which only the main branch is included.
result of `getState` called on the main environment branch at the end of the file, i.e. it
encompasses all modifications for all modes but `local`.
-/
inductive EnvExtension.AsyncMode where
/--
@@ -1262,20 +1235,22 @@ inductive EnvExtension.AsyncMode where
-/
| mainOnly
/--
Accumulates modifications in the `checked` environment like `sync`, but `get/modify/setState` will
panic instead of blocking unless their `asyncDecl` parameter is specified, which will access the
state of the environment branch corresponding to the passed declaration name, if any; see
`AsyncBranch` for a description of the specific state accessed. In other words, at most the
environment branch corresponding to that declaration will be blocked on instead of all prior
branches. The local state can still be accessed by calling `getState` with mode `local`
explicitly.
Accumulates modifications in the `checked` environment like `sync`, but `getState` will panic
instead of blocking. Instead `findStateAsync` should be used, which will access the state of the
environment branch corresponding to the passed declaration name, if any, or otherwise the state
of the current branch. In other words, at most one environment branch will be blocked on instead
of all prior branches. The local state can still be accessed by calling `getState` with mode
`local` explicitly.
This mode is suitable for extensions with map-like state where the key uniquely identifies the
top-level declaration where it could have been set, e.g. because the key on modification is always
the surrounding declaration's name. In particular, this mode is closest to how the environment's
own constant map works which provides `findAsync?` for block-avoiding access.
the surrounding declaration's name. Any calls to `modifyState`/`setState` should assert
`asyncMayContain` with that key to ensure state is never accidentally stored in a branch where it
cannot be found by `findStateAsync`. In particular, this mode is closest to how the environment's
own constant map works which asserts the same predicate on modification and provides `findAsync?`
for block-avoiding access.
-/
| async (branch : AsyncBranch)
| async
deriving Inhabited
abbrev ReplayFn (σ : Type) :=
@@ -1352,24 +1327,6 @@ def mkInitialExtStates : IO (Array EnvExtensionState) := do
let exts envExtensionsRef.get
exts.mapM fun ext => ext.mkInitial
/--
Checks whether `modifyState (asyncDecl := declName)` may be called on an async environment
extension; see `AsyncMode.async` for details.
-/
def asyncMayModify (ext : EnvExtension σ) (env : Environment) (asyncDecl : Name)
(asyncMode := ext.asyncMode) : Bool :=
env.asyncCtx?.all fun ctx =>
match asyncMode with
-- The main env's async context, if any, should be a strict prefix of `asyncDecl`. This does not
-- conclusively check that we are not in some parent branch of `mainEnv` but it covers the most
-- common case of confusing `mainEnv` and `asyncEnv`.
| .async .mainEnv => ctx.mayContain asyncDecl && ctx.declPrefix != asyncDecl
-- The async env's async context should either be `asyncDecl` itself or `asyncDecl` is a nested
-- declaration that is not itself async.
| .async .asyncEnv => ctx.declPrefix == asyncDecl ||
(ctx.mayContain asyncDecl && (env.findAsyncConst? asyncDecl).any (·.exts?.isNone))
| _ => true
/--
Applies the given function to the extension state. See `AsyncMode` for details on how modifications
from different environment branches are reconciled.
@@ -1378,7 +1335,7 @@ Note that in modes `sync` and `async`, `f` will be called twice, on the local an
state.
-/
def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ σ)
(asyncMode := ext.asyncMode) (asyncDecl : Name := .anonymous) : Environment := Id.run do
(asyncMode := ext.asyncMode) : Environment := Id.run do
-- for panics
let _ : Inhabited Environment := env
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
@@ -1391,14 +1348,6 @@ def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ
| .local =>
return { env with base.private.extensions := unsafe ext.modifyStateImpl env.base.private.extensions f }
| _ =>
if asyncMode matches .async _ then
if asyncDecl.isAnonymous then
return panic! "called on `async` extension, must set `asyncDecl` in that case"
if let some ctx := env.asyncCtx? then
if !ext.asyncMayModify (asyncMode := asyncMode) env asyncDecl then
return panic! s!"`asyncDecl` `{asyncDecl}` is outside current context {ctx.declPrefix}"
if ext.replay?.isNone then
if let some (n :: _) := env.asyncCtx?.map (·.realizingStack) then
return panic! s!"environment extension must set `replay?` field to be \
@@ -1410,74 +1359,72 @@ def modifyState {σ : Type} (ext : EnvExtension σ) (env : Environment) (f : σ
Sets the extension state to the given value. See `AsyncMode` for details on how modifications from
different environment branches are reconciled.
-/
def setState {σ : Type} (ext : EnvExtension σ) (env : Environment) (s : σ) (asyncMode := ext.asyncMode) : Environment :=
inline <| modifyState (asyncMode := asyncMode) ext env fun _ => s
def setState {σ : Type} (ext : EnvExtension σ) (env : Environment) (s : σ) : Environment :=
inline <| modifyState ext env fun _ => s
-- `unsafe` fails to infer `Nonempty` here
private unsafe def getStateUnsafe {σ : Type} [Inhabited σ] (ext : EnvExtension σ)
(env : Environment) (asyncMode := ext.asyncMode) (asyncDecl : Name := .anonymous) : σ := Id.run do
(env : Environment) (asyncMode := ext.asyncMode) : σ :=
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
match asyncMode with
| .sync => ext.getStateImpl env.checked.get.extensions
| .async branch =>
if asyncDecl.isAnonymous then
panic! "called on `async` extension, must set `asyncDecl` \
or pass `(asyncMode := .local)` to explicitly access local state"
-- analogous structure to `findAsync?`; see there
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
if env.base.get env |>.constants.contains asyncDecl then
return ext.getStateImpl env.base.private.extensions
-- specialization of the following branch, nested async decls are rare
if let some c := env.asyncConsts.find? asyncDecl then
match branch with
| .asyncEnv =>
if let some exts := c.exts? then
return ext.getStateImpl exts.get
else
return ext.getStateImpl env.base.private.extensions
| .mainEnv =>
if c.isRealized then
if let some exts := c.exts? then
return ext.getStateImpl exts.get
else
return ext.getStateImpl env.base.private.extensions
if let some (c, parent?) := env.asyncConsts.findRecAndParent? asyncDecl then
-- If `parent?` is `none`, the current branch is the parent
let parentExts? := match parent? with
| some c => c.exts?
| none => some <| .pure env.base.private.extensions
if let some exts := (match branch with
-- If the constant is not async, fall back to parent
| .asyncEnv => c.exts? <|> parentExts?
-- If the constant is realized, parent branch is empty and we should always look at `c`. In
-- this specific case, accessing the latter will in particular not block longer than the
-- former.
| .mainEnv => if c.isRealized then c.exts? else parentExts?) then
return ext.getStateImpl exts.get
-- NOTE: if `exts?` is `none`, we should *not* try the following, more expensive branches that
-- will just come to the same conclusion
else if let some c := env.allRealizations.get.find? asyncDecl then
if let some exts := c.exts? then
return ext.getStateImpl exts.get
-- fallback; we could enforce that `asyncDecl` and its extension state always exist but the
-- upside of doing is unclear and it is not true in e.g. the compiler. One alternative would be
-- to add a `getState?` that does not panic in such cases.
ext.getStateImpl env.base.private.extensions
| .sync => ext.getStateImpl env.checked.get.extensions
| .async => panic! "called on `async` extension, use `findStateAsync` \
instead or pass `(asyncMode := .local)` to explicitly access local state"
| _ => ext.getStateImpl env.base.private.extensions
/--
Returns the current extension state. See `AsyncMode` for details on how modifications from
different environment branches are reconciled.
Overriding the extension's default `AsyncMode` is usually not recommended and should be considered
only for important optimizations.
different environment branches are reconciled. Panics if the extension is marked as `async`; see its
documentation for more details. Overriding the extension's default `AsyncMode` is usually not
recommended and should be considered only for important optimizations.
-/
@[implemented_by getStateUnsafe]
opaque getState {σ : Type} [Inhabited σ] (ext : EnvExtension σ) (env : Environment)
(asyncMode := ext.asyncMode) (asyncDecl : Name := .anonymous) : σ
(asyncMode := ext.asyncMode) : σ
-- `unsafe` fails to infer `Nonempty` here
private unsafe def findStateAsyncUnsafe {σ : Type} [Inhabited σ]
(ext : EnvExtension σ) (env : Environment) (declName : Name) : σ := Id.run do
-- analogous structure to `findAsync?`; see there
-- safety: `ext`'s constructor is private, so we can assume the entry at `ext.idx` is of type `σ`
if env.base.get env |>.constants.contains declName then
return ext.getStateImpl env.base.private.extensions
if let some c := env.asyncConsts.find? declName then
if let some exts := c.exts? then
return ext.getStateImpl exts.get
-- NOTE: if `exts?` is `none`, we should *not* try the following, more expensive branches that
-- will just come to the same conclusion
else if let some exts := findRecExts? none env.asyncConsts declName then
return ext.getStateImpl exts.get
else if let some c := env.allRealizations.get.find? declName then
if let some exts := c.exts? then
return ext.getStateImpl exts.get
-- fallback; we could enforce that `findStateAsync` is only used on existing constants but the
-- upside of doing is unclear
ext.getStateImpl env.base.private.extensions
where
/--
Like `AsyncConsts.findRec?`, but if `AsyncConst.exts?` is `none`, returns the extension state of
the surrounding `AsyncConst` instead, which is where state for synchronously added constants is
stored.
-/
findRecExts? (parent? : Option AsyncConst) (aconsts : AsyncConsts) (declName : Name) :
Option (Task (Array EnvExtensionState)) := do
let c aconsts.findPrefix? declName
if c.constInfo.name == declName then
return ( c.exts?.or (parent?.bind (·.exts?)))
let aconsts c.consts.get.get? AsyncConsts
findRecExts? c aconsts declName
/--
Returns the final extension state on the environment branch corresponding to the passed declaration
name, if any, or otherwise the state on the current branch. In other words, at most one environment
branch will be blocked on.
-/
@[implemented_by findStateAsyncUnsafe]
opaque findStateAsync {σ : Type} [Inhabited σ] (ext : EnvExtension σ)
(env : Environment) (declName : Name) : σ
end EnvExtension
@@ -1639,16 +1586,15 @@ def getModuleIREntries {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExte
-- safety: as in `getStateUnsafe`
unsafe (ext.toEnvExtension.getStateImpl env.base.private.irBaseExts).importedEntries[m]!
def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (b : β)
(asyncMode := ext.toEnvExtension.asyncMode) (asyncDecl : Name := .anonymous) : Environment :=
ext.toEnvExtension.modifyState (asyncMode := asyncMode) (asyncDecl := asyncDecl) env fun s =>
def addEntry {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (b : β) : Environment :=
ext.toEnvExtension.modifyState env fun s =>
let state := ext.addEntryFn s.state b;
{ s with state := state }
/-- Get the current state of the given extension in the given environment. -/
def getState {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ) (env : Environment)
(asyncMode := ext.toEnvExtension.asyncMode) (asyncDecl : Name := .anonymous) : σ :=
(ext.toEnvExtension.getState (asyncMode := asyncMode) (asyncDecl := asyncDecl) env).state
(asyncMode := ext.toEnvExtension.asyncMode) : σ :=
(ext.toEnvExtension.getState (asyncMode := asyncMode) env).state
/-- Set the current state of the given extension in the given environment. -/
def setState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env : Environment) (s : σ) : Environment :=
@@ -1659,6 +1605,12 @@ def modifyState {α β σ : Type} (ext : PersistentEnvExtension α β σ) (env :
(asyncMode := ext.toEnvExtension.asyncMode) : Environment :=
ext.toEnvExtension.modifyState (asyncMode := asyncMode) env fun ps => { ps with state := f (ps.state) }
@[inherit_doc EnvExtension.findStateAsync]
def findStateAsync {α β σ : Type} [Inhabited σ] (ext : PersistentEnvExtension α β σ)
(env : Environment) (declPrefix : Name) : σ :=
ext.toEnvExtension.findStateAsync env declPrefix |>.state
end PersistentEnvExtension
builtin_initialize persistentEnvExtensionsRef : IO.Ref (Array (PersistentEnvExtension EnvExtensionEntry EnvExtensionEntry EnvExtensionState)) IO.mkRef #[]
@@ -1784,7 +1736,7 @@ def mkModuleData (env : Environment) (level : OLeanLevel := .private) : IO Modul
let entries := pExts.map fun pExt => Id.run do
-- get state from `checked` at the end if `async`; it would otherwise panic
let mut asyncMode := pExt.toEnvExtension.asyncMode
if asyncMode matches .async _ then
if asyncMode matches .async then
asyncMode := .sync
let state := pExt.getState (asyncMode := asyncMode) env
(pExt.name, pExt.exportEntriesFn env state level)
@@ -1893,13 +1845,13 @@ where
let pExtDescrs persistentEnvExtensionsRef.get
if h : i < pExtDescrs.size then
let extDescr := pExtDescrs[i]
-- Use `sync` to avoid `async` checks; there is only one environment branch at this point
-- anyway.
let s := extDescr.toEnvExtension.getState (asyncMode := .sync) env
-- `local` as `async` does not allow for `getState` but it's all safe here as there is only
-- one environment branch at this point.
let s := extDescr.toEnvExtension.getState (asyncMode := .local) env
let prevSize := ( persistentEnvExtensionsRef.get).size
let prevAttrSize getNumBuiltinAttributes
let newState extDescr.addImportedFn s.importedEntries { env := env, opts := opts }
let mut env := extDescr.toEnvExtension.setState (asyncMode := .sync) env { s with state := newState }
let mut env := extDescr.toEnvExtension.setState env { s with state := newState }
env ensureExtensionsArraySize env
if ( persistentEnvExtensionsRef.get).size > prevSize || ( getNumBuiltinAttributes) > prevAttrSize then
-- This branch is executed when `pExtDescrs[i]` is the extension associated with the `init` attribute, and
@@ -2295,7 +2247,7 @@ private def updateBaseAfterKernelAdd (env : Environment) (kenv : Kernel.Environm
asyncConsts.add {
constInfo := .ofConstantInfo (kenv.find? n |>.get!)
exts? := none
aconstsImpl := .pure <| .mk (α := AsyncConsts) default
consts := .pure <| .mk (α := AsyncConsts) default
}
else asyncConsts
}
@@ -2314,7 +2266,7 @@ def displayStats (env : Environment) : IO Unit := do
IO.println ("extension '" ++ toString extDescr.name ++ "'")
-- get state from `checked` at the end if `async`; it would otherwise panic
let mut asyncMode := extDescr.toEnvExtension.asyncMode
if asyncMode matches .async _ then
if asyncMode matches .async then
asyncMode := .sync
let s := extDescr.toEnvExtension.getState (asyncMode := asyncMode) env
let fmt := extDescr.statsFn s.state
@@ -2482,14 +2434,12 @@ def realizeConst (env : Environment) (forConst : Name) (constName : Name)
let numNewPrivateConsts := realizeEnv'.asyncConstsMap.private.size - realizeEnv.asyncConstsMap.private.size
let newPrivateConsts := realizeEnv'.asyncConstsMap.private.revList.take numNewPrivateConsts |>.reverse
let newPrivateConsts := newPrivateConsts.map fun c =>
let c := { c with isRealized := true }
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.base.private.extensions }
else c
let numNewPublicConsts := realizeEnv'.asyncConstsMap.public.size - realizeEnv.asyncConstsMap.public.size
let newPublicConsts := realizeEnv'.asyncConstsMap.public.revList.take numNewPublicConsts |>.reverse
let newPublicConsts := newPublicConsts.map fun c =>
let c := { c with isRealized := true }
if c.exts?.isNone then
{ c with exts? := some <| .pure realizeEnv'.base.private.extensions }
else c

View File

@@ -716,20 +716,6 @@ def mkIffOfEq (h : Expr) : MetaM Expr := do
else
mkAppM ``Iff.of_eq #[h]
/--
Given proofs `hᵢ : pᵢ`, returns a proof for `p₁ ∧ ... ∧ pₙ`.
Roughly, `mkAndIntroN hs : mkAndN (← hs.mapM inferType)`.
-/
def mkAndIntroN (hs : List Expr) : MetaM Expr := (·.1) <$> go hs
where
go : List Expr MetaM (Expr × Expr)
| [] => return (mkConst ``True.intro, mkConst ``True)
| [h] => return (h, inferType h)
| h :: hs => do
let (h', p') go hs
let p inferType h
return (mkApp4 (mkConst ``And.intro) p p' h h', mkApp2 (mkConst ``And) p p')
builtin_initialize do
registerTraceClass `Meta.appBuilder
registerTraceClass `Meta.appBuilder.result (inherited := true)

View File

@@ -2483,9 +2483,9 @@ output are reported at all callers via `Core.logSnapshotTask` (so that the locat
diagnostics is deterministic). Note that, as `realize` is run using the options at declaration time
of `forConst`, trace options must be set prior to that (or, for imported constants, on the cmdline)
in order to be active. The environment extension state at the end of `realize` is available to each
caller via `EnvExtension.getState (asyncDecl := constName)`. If `realize` throws an exception or
fails to add `constName` to the environment, an appropriate diagnostic is reported to all callers
but no constants are added to the environment.
caller via `EnvExtension.findStateAsync` for `constName`. If `realize` throws an exception or fails
to add `constName` to the environment, an appropriate diagnostic is reported to all callers but no
constants are added to the environment.
-/
def realizeConst (forConst : Name) (constName : Name) (realize : MetaM Unit) :
MetaM Unit := do

View File

@@ -462,7 +462,7 @@ def mkHCongrWithArityForConst? (declName : Name) (levels : List Level) (numArgs
try
let suffix := hcongrThmSuffixBasePrefix ++ toString numArgs
let thmName := Name.str declName suffix
unless ( getEnv).containsOnBranch thmName do
unless ( getEnv).contains thmName do
let _ executeReservedNameAction thmName
let proof := mkConst thmName levels
let type inferType proof
@@ -479,7 +479,7 @@ same congruence theorem over and over again.
def mkCongrSimpForConst? (declName : Name) (levels : List Level) : MetaM (Option CongrTheorem) := do
let thmName := Name.str declName congrSimpSuffix
try
unless ( getEnv).containsOnBranch thmName do
unless ( getEnv).contains thmName do
let _ executeReservedNameAction thmName
let proof := mkConst thmName levels
let type inferType proof

View File

@@ -23,6 +23,5 @@ def mkCasesOn (declName : Name) : MetaM Unit := do
addDecl decl
setReducibleAttribute name
modifyEnv fun env => markAuxRecursor env name
enableRealizationsForConst name
end Lean

View File

@@ -49,7 +49,7 @@ This information is populated by the `PreDefinition` module, but the simplifier
uses when unfolding declarations.
-/
builtin_initialize recExt : TagDeclarationExtension
mkTagDeclarationExtension `recExt (asyncMode := .async .asyncEnv)
mkTagDeclarationExtension `recExt (asyncMode := .async)
/--
Marks the given declaration as recursive.

View File

@@ -287,17 +287,14 @@ partial def trySubstVarsAndContradiction (mvarId : MVarId) (forbidden : FVarIdSe
private def processNextEq : M Bool := do
let s get
s.mvarId.withContext do
-- If the goal is contradictory, the hypothesis is redundant.
if ( contradiction s.mvarId) then
return false
if let eq :: eqs := s.eqs then
modify fun s => { s with eqs }
let eqType inferType (mkFVar eq)
-- See `substRHS`. Recall that if `rhs` is a variable then if must be in `s.xs`
if let some (_, lhs, rhs) matchEq? eqType then
-- Common case: Different constructors
match ( isConstructorApp? lhs), ( isConstructorApp? rhs) with
| some lhsCtor, some rhsCtor =>
if lhsCtor.name != rhsCtor.name then
return false -- If the constructors are different, we can discard the hypothesis even if it a heterogeneous equality
| _,_ => pure ()
if ( isDefEq lhs rhs) then
return true
if rhs.isFVar && s.xs.contains rhs.fvarId! then
@@ -747,9 +744,9 @@ def getEquationsForImpl (matchDeclName : Name) : MetaM MatchEqns := do
let splitterName := baseName ++ `splitter
-- NOTE: `go` will generate both splitter and equations but we use the splitter as the "key" for
-- `realizeConst` as well as for looking up the resultant environment extension state via
-- `getState`.
-- `findStateAsync`.
realizeConst matchDeclName splitterName (go baseName splitterName)
return matchEqnsExt.getState (asyncMode := .async .asyncEnv) (asyncDecl := splitterName) ( getEnv) |>.map.find! matchDeclName
return matchEqnsExt.findStateAsync ( getEnv) splitterName |>.map.find! matchDeclName
where go baseName splitterName := withConfig (fun c => { c with etaStruct := .none }) do
let constInfo getConstInfo matchDeclName
let us := constInfo.levelParams.map mkLevelParam
@@ -846,7 +843,7 @@ def isCongrEqnReservedNameSuffix (s : String) : Bool :=
/- We generate the equations and splitter on demand, and do not save them on .olean files. -/
builtin_initialize matchCongrEqnsExt : EnvExtension (PHashMap Name (Array Name))
-- Using `local` allows us to use the extension in `realizeConst` without specifying `replay?`.
-- The resulting state can still be accessed on the generated declarations using `.asyncEnv`;
-- The resulting state can still be accessed on the generated declarations using `findStateAsync`;
-- see below
registerEnvExtension (pure {}) (asyncMode := .local)
@@ -869,7 +866,7 @@ def genMatchCongrEqns (matchDeclName : Name) : MetaM (Array Name) := do
let baseName := mkPrivateName ( getEnv) matchDeclName
let firstEqnName := .str baseName congrEqn1ThmSuffix
realizeConst matchDeclName firstEqnName (go baseName)
return matchCongrEqnsExt.getState (asyncMode := .async .asyncEnv) (asyncDecl := firstEqnName) ( getEnv) |>.find! matchDeclName
return matchCongrEqnsExt.findStateAsync ( getEnv) firstEqnName |>.find! matchDeclName
where go baseName := withConfig (fun c => { c with etaStruct := .none }) do
withConfig (fun c => { c with etaStruct := .none }) do
let constInfo getConstInfo matchDeclName

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