mirror of
https://github.com/leanprover/lean4.git
synced 2026-03-24 05:44:15 +00:00
Compare commits
1 Commits
expose_fil
...
grind_warn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45ce6dda4b |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 α} :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
/--
|
||||
|
||||
@@ -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
@@ -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 -/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 : α} :
|
||||
|
||||
@@ -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 -/
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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 : α} :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)])
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
/--
|
||||
|
||||
@@ -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 :=
|
||||
|
||||
@@ -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 :=
|
||||
|
||||
@@ -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. -/
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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} :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 [*]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
/-!
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
134
src/Init/System/Mutex.lean
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
/--
|
||||
|
||||
@@ -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)
|
||||
|
||||
/--
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
/-
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 :=
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 =>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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}'"
|
||||
|
||||
@@ -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 hφ => do
|
||||
let prf ← kSuccess φ hφ goal
|
||||
let (a, prf) ← kSuccess φ hφ goal
|
||||
let prf ← mkLambdaFVars #[hφ] 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!]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 hφ
|
||||
-- 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 hφ
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user