Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. Β See raw diff
- .gitattributes +27 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/WP/Basic.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Internal/UV/System.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Operations/Replicate.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/DateTime/PlainDateTime.olean +3 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Init/Grind/Ordered/Order.lean +118 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Lsp/Utf16.lean +120 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Lsp/Window.lean +48 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Lsp/Workspace.lean +73 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Xml/Basic.lean +41 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Xml/Parser.lean +487 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/DocString/Add.lean +61 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/DocString/Extension.lean +81 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/DocString/Links.lean +171 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/App.lean +1854 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Arg.lean +68 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Attributes.lean +71 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/AutoBound.lean +51 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/AuxDef.lean +36 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BinderPredicates.lean +43 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Binders.lean +957 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BindersUtil.lean +73 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinCommand.lean +676 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinEvalCommand.lean +277 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinNotation.lean +534 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinTerm.lean +386 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Calc.lean +174 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/CheckTactic.lean +86 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Command.lean +891 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/ComputedFields.lean +246 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Config.lean +61 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclModifiers.lean +306 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclNameGen.lean +264 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclUtil.lean +86 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Declaration.lean +347 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclarationRange.lean +71 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DefView.lean +232 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Deriving.lean +19 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Do.lean +1827 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/ElabRules.lean +102 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/ErrorExplanation.lean +138 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Eval.lean +20 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Exception.lean +68 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Extra.lean +588 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Frontend.lean +218 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/GenInjective.lean +17 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/GuardMsgs.lean +235 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Import.lean +101 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Inductive.lean +301 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/InfoTree.lean +9 -0
.gitattributes
CHANGED
|
@@ -4432,3 +4432,30 @@ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Sat/AIG/RelabelNat.olean f
|
|
| 4432 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/PostCond.olean filter=lfs diff=lfs merge=lfs -text
|
| 4433 |
external/alphageometry/.venv-ag/Lib/site-packages/scipy/fftpack/tests/fftw_double_ref.npz filter=lfs diff=lfs merge=lfs -text
|
| 4434 |
external/alphageometry/.venv-ag/Lib/site-packages/scipy/fftpack/tests/fftw_longdouble_ref.npz filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4432 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/PostCond.olean filter=lfs diff=lfs merge=lfs -text
|
| 4433 |
external/alphageometry/.venv-ag/Lib/site-packages/scipy/fftpack/tests/fftw_double_ref.npz filter=lfs diff=lfs merge=lfs -text
|
| 4434 |
external/alphageometry/.venv-ag/Lib/site-packages/scipy/fftpack/tests/fftw_longdouble_ref.npz filter=lfs diff=lfs merge=lfs -text
|
| 4435 |
+
external/alphageometry/.venv-ag/Scripts/f2py.exe filter=lfs diff=lfs merge=lfs -text
|
| 4436 |
+
external/alphageometry/.venv-ag/Scripts/fonttools.exe filter=lfs diff=lfs merge=lfs -text
|
| 4437 |
+
external/alphageometry/.venv-ag/Scripts/numpy-config.exe filter=lfs diff=lfs merge=lfs -text
|
| 4438 |
+
external/alphageometry/.venv-ag/Scripts/pip.exe filter=lfs diff=lfs merge=lfs -text
|
| 4439 |
+
external/alphageometry/.venv-ag/Scripts/pip3.10.exe filter=lfs diff=lfs merge=lfs -text
|
| 4440 |
+
external/alphageometry/.venv-ag/Scripts/pip3.exe filter=lfs diff=lfs merge=lfs -text
|
| 4441 |
+
external/alphageometry/.venv-ag/Scripts/pyftmerge.exe filter=lfs diff=lfs merge=lfs -text
|
| 4442 |
+
external/alphageometry/.venv-ag/Scripts/pyftsubset.exe filter=lfs diff=lfs merge=lfs -text
|
| 4443 |
+
external/alphageometry/.venv-ag/Scripts/python.exe filter=lfs diff=lfs merge=lfs -text
|
| 4444 |
+
external/alphageometry/.venv-ag/Scripts/pythonw.exe filter=lfs diff=lfs merge=lfs -text
|
| 4445 |
+
external/alphageometry/.venv-ag/Scripts/ttx.exe filter=lfs diff=lfs merge=lfs -text
|
| 4446 |
+
external/alphageometry/.venv-ag/Scripts/wheel.exe filter=lfs diff=lfs merge=lfs -text
|
| 4447 |
+
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Internal/UV/System.olean filter=lfs diff=lfs merge=lfs -text
|
| 4448 |
+
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/DateTime/PlainDateTime.olean filter=lfs diff=lfs merge=lfs -text
|
| 4449 |
+
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/WP/Basic.olean filter=lfs diff=lfs merge=lfs -text
|
| 4450 |
+
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Operations/Replicate.olean filter=lfs diff=lfs merge=lfs -text
|
| 4451 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_1000.model filter=lfs diff=lfs merge=lfs -text
|
| 4452 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_128000.model filter=lfs diff=lfs merge=lfs -text
|
| 4453 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_32000.model filter=lfs diff=lfs merge=lfs -text
|
| 4454 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_4000.model filter=lfs diff=lfs merge=lfs -text
|
| 4455 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_512.model filter=lfs diff=lfs merge=lfs -text
|
| 4456 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_64000.model filter=lfs diff=lfs merge=lfs -text
|
| 4457 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_8000.model filter=lfs diff=lfs merge=lfs -text
|
| 4458 |
+
external/alphageometry/meliad_lib/meliad/transformer/vocabs/pg19train_bpe_96000.model filter=lfs diff=lfs merge=lfs -text
|
| 4459 |
+
frontend/file_00000000bcfc624381c37949ccd77bc9.png filter=lfs diff=lfs merge=lfs -text
|
| 4460 |
+
frontend/file_00000000cf6462469688bc5199fed92b.png filter=lfs diff=lfs merge=lfs -text
|
| 4461 |
+
frontend/IMG_20250618_170452.jpg filter=lfs diff=lfs merge=lfs -text
|
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Do/WP/Basic.olean
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e9ec7a91d4d492bea4023c88138e4274ad0fd17c51385ffb706b56b7dd945576
|
| 3 |
+
size 301128
|
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Internal/UV/System.olean
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d18b1027d6e9c1432697fa689cc21ba16dc92f429e56e440f58a319cce295c74
|
| 3 |
+
size 736624
|
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/BVExpr/Circuit/Lemmas/Operations/Replicate.olean
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ea58b608189883e9ec75733caae606328846567f9444b65e130f6f56a7c95dd6
|
| 3 |
+
size 324992
|
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Time/DateTime/PlainDateTime.olean
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:45800e87fb690735a4ea0044e72e1c7b31b1a4e05854b3d0d66b1e28c04eaab0
|
| 3 |
+
size 704216
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Init/Grind/Ordered/Order.lean
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Lean FRO, LLC. or its affiliates. All Rights Reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Kim Morrison
|
| 5 |
+
-/
|
| 6 |
+
module
|
| 7 |
+
|
| 8 |
+
prelude
|
| 9 |
+
public import Init.Data.Int.Order
|
| 10 |
+
|
| 11 |
+
public section
|
| 12 |
+
|
| 13 |
+
namespace Lean.Grind
|
| 14 |
+
|
| 15 |
+
/-- A preorder is a reflexive, transitive relation `β€` with `a < b` defined in the obvious way. -/
|
| 16 |
+
class Preorder (Ξ± : Type u) extends LE Ξ±, LT Ξ± where
|
| 17 |
+
/-- The less-than-or-equal relation is reflexive. -/
|
| 18 |
+
le_refl : β a : Ξ±, a β€ a
|
| 19 |
+
/-- The less-than-or-equal relation is transitive. -/
|
| 20 |
+
le_trans : β {a b c : Ξ±}, a β€ b β b β€ c β a β€ c
|
| 21 |
+
lt := fun a b => a β€ b β§ Β¬b β€ a
|
| 22 |
+
/-- The less-than relation is determined by the less-than-or-equal relation. -/
|
| 23 |
+
lt_iff_le_not_le : β {a b : Ξ±}, a < b β a β€ b β§ Β¬b β€ a := by intros; rfl
|
| 24 |
+
|
| 25 |
+
namespace Preorder
|
| 26 |
+
|
| 27 |
+
variable {Ξ± : Type u} [Preorder Ξ±]
|
| 28 |
+
|
| 29 |
+
theorem le_of_lt {a b : Ξ±} (h : a < b) : a β€ b := (lt_iff_le_not_le.mp h).1
|
| 30 |
+
|
| 31 |
+
theorem lt_of_lt_of_le {a b c : Ξ±} (hβ : a < b) (hβ : b β€ c) : a < c := by
|
| 32 |
+
simp [lt_iff_le_not_le] at hβ β’
|
| 33 |
+
exact β¨le_trans hβ.1 hβ, fun h => hβ.2 (le_trans hβ h)β©
|
| 34 |
+
|
| 35 |
+
theorem lt_of_le_of_lt {a b c : Ξ±} (hβ : a β€ b) (hβ : b < c) : a < c := by
|
| 36 |
+
simp [lt_iff_le_not_le] at hβ β’
|
| 37 |
+
exact β¨le_trans hβ hβ.1, fun h => hβ.2 (le_trans h hβ)β©
|
| 38 |
+
|
| 39 |
+
theorem lt_trans {a b c : Ξ±} (hβ : a < b) (hβ : b < c) : a < c :=
|
| 40 |
+
lt_of_lt_of_le hβ (le_of_lt hβ)
|
| 41 |
+
|
| 42 |
+
theorem lt_irrefl (a : Ξ±) : Β¬ (a < a) := by
|
| 43 |
+
intro h
|
| 44 |
+
simp [lt_iff_le_not_le] at h
|
| 45 |
+
|
| 46 |
+
theorem ne_of_lt {a b : Ξ±} (h : a < b) : a β b :=
|
| 47 |
+
fun w => lt_irrefl a (w.symm βΈ h)
|
| 48 |
+
|
| 49 |
+
theorem ne_of_gt {a b : Ξ±} (h : a > b) : a β b :=
|
| 50 |
+
fun w => lt_irrefl b (w.symm βΈ h)
|
| 51 |
+
|
| 52 |
+
theorem not_ge_of_lt {a b : Ξ±} (h : a < b) : Β¬b β€ a :=
|
| 53 |
+
fun w => lt_irrefl a (lt_of_lt_of_le h w)
|
| 54 |
+
|
| 55 |
+
theorem not_gt_of_lt {a b : Ξ±} (h : a < b) : Β¬a > b :=
|
| 56 |
+
fun w => lt_irrefl a (lt_trans h w)
|
| 57 |
+
|
| 58 |
+
end Preorder
|
| 59 |
+
|
| 60 |
+
/-- A partial order is a preorder with the additional property that `a β€ b` and `b β€ a` implies `a = b`. -/
|
| 61 |
+
class PartialOrder (Ξ± : Type u) extends Preorder Ξ± where
|
| 62 |
+
/-- The less-than-or-equal relation is antisymmetric. -/
|
| 63 |
+
le_antisymm : β {a b : Ξ±}, a β€ b β b β€ a β a = b
|
| 64 |
+
|
| 65 |
+
namespace PartialOrder
|
| 66 |
+
|
| 67 |
+
variable {Ξ± : Type u} [PartialOrder Ξ±]
|
| 68 |
+
|
| 69 |
+
theorem le_iff_lt_or_eq {a b : Ξ±} : a β€ b β a < b β¨ a = b := by
|
| 70 |
+
constructor
|
| 71 |
+
Β· intro h
|
| 72 |
+
rw [Preorder.lt_iff_le_not_le, Classical.or_iff_not_imp_right]
|
| 73 |
+
exact fun w => β¨h, fun w' => w (le_antisymm h w')β©
|
| 74 |
+
Β· intro h
|
| 75 |
+
cases h with
|
| 76 |
+
| inl h => exact Preorder.le_of_lt h
|
| 77 |
+
| inr h => subst h; exact Preorder.le_refl a
|
| 78 |
+
|
| 79 |
+
end PartialOrder
|
| 80 |
+
|
| 81 |
+
/-- A linear order is a partial order with the additional property that every pair of elements is comparable. -/
|
| 82 |
+
class LinearOrder (Ξ± : Type u) extends PartialOrder Ξ± where
|
| 83 |
+
/-- For every two elements `a` and `b`, either `a β€ b` or `b β€ a`. -/
|
| 84 |
+
le_total : β a b : Ξ±, a β€ b β¨ b β€ a
|
| 85 |
+
|
| 86 |
+
namespace LinearOrder
|
| 87 |
+
|
| 88 |
+
variable {Ξ± : Type u} [LinearOrder Ξ±]
|
| 89 |
+
|
| 90 |
+
theorem trichotomy (a b : Ξ±) : a < b β¨ a = b β¨ b < a := by
|
| 91 |
+
cases LinearOrder.le_total a b with
|
| 92 |
+
| inl h =>
|
| 93 |
+
rw [PartialOrder.le_iff_lt_or_eq] at h
|
| 94 |
+
cases h with
|
| 95 |
+
| inl h => left; exact h
|
| 96 |
+
| inr h => right; left; exact h
|
| 97 |
+
| inr h =>
|
| 98 |
+
rw [PartialOrder.le_iff_lt_or_eq] at h
|
| 99 |
+
cases h with
|
| 100 |
+
| inl h => right; right; exact h
|
| 101 |
+
| inr h => right; left; exact h.symm
|
| 102 |
+
|
| 103 |
+
theorem le_of_not_lt {Ξ±} [LinearOrder Ξ±] {a b : Ξ±} (h : Β¬ a < b) : b β€ a := by
|
| 104 |
+
cases LinearOrder.trichotomy a b
|
| 105 |
+
next => contradiction
|
| 106 |
+
next h => apply PartialOrder.le_iff_lt_or_eq.mpr; cases h <;> simp [*]
|
| 107 |
+
|
| 108 |
+
theorem lt_of_not_le {Ξ±} [LinearOrder Ξ±] {a b : Ξ±} (h : Β¬ a β€ b) : b < a := by
|
| 109 |
+
cases LinearOrder.trichotomy a b
|
| 110 |
+
next hβ hβ => have := Preorder.lt_iff_le_not_le.mp hβ; simp [h] at this
|
| 111 |
+
next h =>
|
| 112 |
+
cases h
|
| 113 |
+
next h => subst a; exact False.elim <| h (Preorder.le_refl b)
|
| 114 |
+
next => assumption
|
| 115 |
+
|
| 116 |
+
end LinearOrder
|
| 117 |
+
|
| 118 |
+
end Lean.Grind
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Lsp/Utf16.lean
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Marc Huisinga. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
|
| 5 |
+
Authors: Marc Huisinga, Wojciech Nawrocki
|
| 6 |
+
-/
|
| 7 |
+
prelude
|
| 8 |
+
import Init.Data.String
|
| 9 |
+
import Lean.Data.Lsp.Basic
|
| 10 |
+
import Lean.Data.Position
|
| 11 |
+
import Lean.DeclarationRange
|
| 12 |
+
|
| 13 |
+
/-! LSP uses UTF-16 for indexing, so we need to provide some primitives
|
| 14 |
+
to interact with Lean strings using UTF-16 indices. -/
|
| 15 |
+
|
| 16 |
+
namespace Char
|
| 17 |
+
|
| 18 |
+
/-- Returns the number of bytes required to encode this `Char` in UTF-16. -/
|
| 19 |
+
def utf16Size (c : Char) : UInt32 :=
|
| 20 |
+
if c.val β€ 0xFFFF then 1 else 2
|
| 21 |
+
|
| 22 |
+
end Char
|
| 23 |
+
|
| 24 |
+
namespace String
|
| 25 |
+
|
| 26 |
+
private def csize16 (c : Char) : Nat :=
|
| 27 |
+
c.utf16Size.toNat
|
| 28 |
+
|
| 29 |
+
def utf16Length (s : String) : Nat :=
|
| 30 |
+
s.foldr (fun c acc => csize16 c + acc) 0
|
| 31 |
+
|
| 32 |
+
private def codepointPosToUtf16PosFromAux (s : String) : Nat β Pos β Nat β Nat
|
| 33 |
+
| 0, _, utf16pos => utf16pos
|
| 34 |
+
| cp+1, utf8pos, utf16pos => codepointPosToUtf16PosFromAux s cp (s.next utf8pos) (utf16pos + csize16 (s.get utf8pos))
|
| 35 |
+
|
| 36 |
+
/-- Computes the UTF-16 offset of the `n`-th Unicode codepoint
|
| 37 |
+
in the substring of `s` starting at UTF-8 offset `off`.
|
| 38 |
+
Yes, this is actually useful.-/
|
| 39 |
+
def codepointPosToUtf16PosFrom (s : String) (n : Nat) (off : Pos) : Nat :=
|
| 40 |
+
codepointPosToUtf16PosFromAux s n off 0
|
| 41 |
+
|
| 42 |
+
def codepointPosToUtf16Pos (s : String) (pos : Nat) : Nat :=
|
| 43 |
+
codepointPosToUtf16PosFrom s pos 0
|
| 44 |
+
|
| 45 |
+
private partial def utf16PosToCodepointPosFromAux (s : String) : Nat β Pos β Nat β Nat
|
| 46 |
+
| 0, _, cp => cp
|
| 47 |
+
| utf16pos, utf8pos, cp => utf16PosToCodepointPosFromAux s (utf16pos - csize16 (s.get utf8pos)) (s.next utf8pos) (cp + 1)
|
| 48 |
+
|
| 49 |
+
/-- Computes the position of the Unicode codepoint at UTF-16 offset
|
| 50 |
+
`utf16pos` in the substring of `s` starting at UTF-8 offset `off`. -/
|
| 51 |
+
def utf16PosToCodepointPosFrom (s : String) (utf16pos : Nat) (off : Pos) : Nat :=
|
| 52 |
+
utf16PosToCodepointPosFromAux s utf16pos off 0
|
| 53 |
+
|
| 54 |
+
def utf16PosToCodepointPos (s : String) (pos : Nat) : Nat :=
|
| 55 |
+
utf16PosToCodepointPosFrom s pos 0
|
| 56 |
+
|
| 57 |
+
/-- Starting at `utf8pos`, finds the UTF-8 offset of the `p`-th codepoint. -/
|
| 58 |
+
def codepointPosToUtf8PosFrom (s : String) : String.Pos β Nat β String.Pos
|
| 59 |
+
| utf8pos, 0 => utf8pos
|
| 60 |
+
| utf8pos, p+1 => codepointPosToUtf8PosFrom s (s.next utf8pos) p
|
| 61 |
+
|
| 62 |
+
end String
|
| 63 |
+
|
| 64 |
+
namespace Lean
|
| 65 |
+
namespace FileMap
|
| 66 |
+
|
| 67 |
+
private def lineStartPos (text : FileMap) (line : Nat) : String.Pos :=
|
| 68 |
+
if h : line < text.positions.size then
|
| 69 |
+
text.positions[line]
|
| 70 |
+
else if text.positions.isEmpty then
|
| 71 |
+
0
|
| 72 |
+
else
|
| 73 |
+
text.positions.back!
|
| 74 |
+
|
| 75 |
+
/-- Computes an UTF-8 offset into `text.source`
|
| 76 |
+
from an LSP-style 0-indexed (ln, col) position. -/
|
| 77 |
+
def lspPosToUtf8Pos (text : FileMap) (pos : Lsp.Position) : String.Pos :=
|
| 78 |
+
let lineStartPos := lineStartPos text pos.line
|
| 79 |
+
let chr := text.source.utf16PosToCodepointPosFrom pos.character lineStartPos
|
| 80 |
+
text.source.codepointPosToUtf8PosFrom lineStartPos chr
|
| 81 |
+
|
| 82 |
+
def leanPosToLspPos (text : FileMap) : Lean.Position β Lsp.Position
|
| 83 |
+
| β¨line, colβ© =>
|
| 84 |
+
β¨line - 1, text.source.codepointPosToUtf16PosFrom col (lineStartPos text (line - 1))β©
|
| 85 |
+
|
| 86 |
+
def utf8PosToLspPos (text : FileMap) (pos : String.Pos) : Lsp.Position :=
|
| 87 |
+
text.leanPosToLspPos (text.toPosition pos)
|
| 88 |
+
|
| 89 |
+
/-- Gets the LSP range from a `String.Range`. -/
|
| 90 |
+
def utf8RangeToLspRange (text : FileMap) (range : String.Range) : Lsp.Range :=
|
| 91 |
+
{ start := text.utf8PosToLspPos range.start, Β«endΒ» := text.utf8PosToLspPos range.stop }
|
| 92 |
+
|
| 93 |
+
def lspRangeToUtf8Range (text : FileMap) (range : Lsp.Range) : String.Range :=
|
| 94 |
+
{ start := text.lspPosToUtf8Pos range.start, stop := text.lspPosToUtf8Pos range.end }
|
| 95 |
+
|
| 96 |
+
end FileMap
|
| 97 |
+
|
| 98 |
+
def DeclarationRange.ofFilePositions (text : FileMap) (pos : Position) (endPos : Position)
|
| 99 |
+
: DeclarationRange := {
|
| 100 |
+
pos,
|
| 101 |
+
charUtf16 := text.leanPosToLspPos pos |>.character
|
| 102 |
+
endPos,
|
| 103 |
+
endCharUtf16 := text.leanPosToLspPos endPos |>.character
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
def DeclarationRange.ofStringPositions (text : FileMap) (pos : String.Pos) (endPos : String.Pos)
|
| 107 |
+
: DeclarationRange :=
|
| 108 |
+
.ofFilePositions text (text.toPosition pos) (text.toPosition endPos)
|
| 109 |
+
|
| 110 |
+
/--
|
| 111 |
+
Convert the Lean `DeclarationRange` to an LSP `Range` by turning the 1-indexed line numbering into a
|
| 112 |
+
0-indexed line numbering and converting the character offset within the line to a UTF-16 indexed
|
| 113 |
+
offset.
|
| 114 |
+
-/
|
| 115 |
+
def DeclarationRange.toLspRange (r : DeclarationRange) : Lsp.Range := {
|
| 116 |
+
start := β¨r.pos.line - 1, r.charUtf16β©
|
| 117 |
+
Β«endΒ» := β¨r.endPos.line - 1, r.endCharUtf16β©
|
| 118 |
+
}
|
| 119 |
+
|
| 120 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Lsp/Window.lean
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Marc Huisinga
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.Json
|
| 8 |
+
|
| 9 |
+
open Lean
|
| 10 |
+
|
| 11 |
+
inductive MessageType where
|
| 12 |
+
| error
|
| 13 |
+
| warning
|
| 14 |
+
| info
|
| 15 |
+
| log
|
| 16 |
+
|
| 17 |
+
instance : FromJson MessageType where
|
| 18 |
+
fromJson?
|
| 19 |
+
| (1 : Nat) => .ok .error
|
| 20 |
+
| (2 : Nat) => .ok .warning
|
| 21 |
+
| (3 : Nat) => .ok .info
|
| 22 |
+
| (4 : Nat) => .ok .log
|
| 23 |
+
| _ => .error "Unknown MessageType ID"
|
| 24 |
+
|
| 25 |
+
instance : ToJson MessageType where
|
| 26 |
+
toJson
|
| 27 |
+
| .error => 1
|
| 28 |
+
| .warning => 2
|
| 29 |
+
| .info => 3
|
| 30 |
+
| .log => 4
|
| 31 |
+
|
| 32 |
+
structure ShowMessageParams where
|
| 33 |
+
type : MessageType
|
| 34 |
+
message : String
|
| 35 |
+
deriving FromJson, ToJson
|
| 36 |
+
|
| 37 |
+
structure MessageActionItem where
|
| 38 |
+
title : String
|
| 39 |
+
deriving FromJson, ToJson
|
| 40 |
+
|
| 41 |
+
structure ShowMessageRequestParams where
|
| 42 |
+
type : MessageType
|
| 43 |
+
message : String
|
| 44 |
+
actions? : Option (Array MessageActionItem)
|
| 45 |
+
deriving FromJson, ToJson
|
| 46 |
+
|
| 47 |
+
def ShowMessageResponse := Option MessageActionItem
|
| 48 |
+
deriving FromJson, ToJson
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Lsp/Workspace.lean
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Wojciech Nawrocki. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
|
| 5 |
+
Authors: Wojciech Nawrocki
|
| 6 |
+
-/
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Data.Lsp.Basic
|
| 9 |
+
import Lean.Data.Json
|
| 10 |
+
|
| 11 |
+
namespace Lean
|
| 12 |
+
namespace Lsp
|
| 13 |
+
|
| 14 |
+
open Json
|
| 15 |
+
|
| 16 |
+
structure WorkspaceFolder where
|
| 17 |
+
uri : DocumentUri
|
| 18 |
+
name : String
|
| 19 |
+
deriving ToJson, FromJson
|
| 20 |
+
|
| 21 |
+
-- TODO(WN):
|
| 22 |
+
-- WorkspaceFoldersServerCapabilities,
|
| 23 |
+
-- DidChangeWorkspaceFoldersParams,
|
| 24 |
+
-- WorkspaceFoldersChangeEvent
|
| 25 |
+
|
| 26 |
+
structure FileSystemWatcher where
|
| 27 |
+
globPattern : String
|
| 28 |
+
kind : Option Nat := none
|
| 29 |
+
deriving FromJson, ToJson
|
| 30 |
+
|
| 31 |
+
namespace FileSystemWatcher
|
| 32 |
+
|
| 33 |
+
-- Bit flags for `FileSystemWatcher.kind`
|
| 34 |
+
def create := 1
|
| 35 |
+
def change := 2
|
| 36 |
+
def delete := 4
|
| 37 |
+
|
| 38 |
+
end FileSystemWatcher
|
| 39 |
+
|
| 40 |
+
structure DidChangeWatchedFilesRegistrationOptions where
|
| 41 |
+
watchers : Array FileSystemWatcher
|
| 42 |
+
deriving FromJson, ToJson
|
| 43 |
+
|
| 44 |
+
inductive FileChangeType
|
| 45 |
+
| Created
|
| 46 |
+
| Changed
|
| 47 |
+
| Deleted
|
| 48 |
+
|
| 49 |
+
instance : FromJson FileChangeType where
|
| 50 |
+
fromJson? j := do
|
| 51 |
+
match (β fromJson? j : Nat) with
|
| 52 |
+
| 1 => return FileChangeType.Created
|
| 53 |
+
| 2 => return FileChangeType.Changed
|
| 54 |
+
| 3 => return FileChangeType.Deleted
|
| 55 |
+
| _ => throw s!"expected 1, 2, or 3, got {j}"
|
| 56 |
+
|
| 57 |
+
instance : ToJson FileChangeType where
|
| 58 |
+
toJson
|
| 59 |
+
| FileChangeType.Created => toJson 1
|
| 60 |
+
| FileChangeType.Changed => toJson 2
|
| 61 |
+
| FileChangeType.Deleted => toJson 3
|
| 62 |
+
|
| 63 |
+
structure FileEvent where
|
| 64 |
+
uri : DocumentUri
|
| 65 |
+
type : FileChangeType
|
| 66 |
+
deriving FromJson, ToJson
|
| 67 |
+
|
| 68 |
+
structure DidChangeWatchedFilesParams where
|
| 69 |
+
changes : Array FileEvent
|
| 70 |
+
deriving FromJson, ToJson
|
| 71 |
+
|
| 72 |
+
end Lsp
|
| 73 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Xml/Basic.lean
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Author: Dany Fabian
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.RBMap
|
| 8 |
+
import Init.Data.ToString.Macro
|
| 9 |
+
|
| 10 |
+
namespace Lean
|
| 11 |
+
namespace Xml
|
| 12 |
+
|
| 13 |
+
def Attributes := RBMap String String compare
|
| 14 |
+
instance : ToString Attributes := β¨Ξ» as => as.fold (Ξ» s n v => s ++ s!" {n}=\"{v}\"") ""β©
|
| 15 |
+
|
| 16 |
+
mutual
|
| 17 |
+
inductive Element
|
| 18 |
+
| Element
|
| 19 |
+
(name : String)
|
| 20 |
+
(attributes : Attributes)
|
| 21 |
+
(content : Array Content)
|
| 22 |
+
|
| 23 |
+
inductive Content
|
| 24 |
+
| Element (element : Element)
|
| 25 |
+
| Comment (comment : String)
|
| 26 |
+
| Character (content : String)
|
| 27 |
+
deriving Inhabited
|
| 28 |
+
end
|
| 29 |
+
|
| 30 |
+
mutual
|
| 31 |
+
private partial def eToString : Element β String
|
| 32 |
+
| Element.Element n a c => s!"<{n}{a}>{c.map cToString |>.foldl (Β· ++ Β·) ""}</{n}>"
|
| 33 |
+
|
| 34 |
+
private partial def cToString : Content β String
|
| 35 |
+
| Content.Element e => eToString e
|
| 36 |
+
| Content.Comment c => s!"<!--{c}-->"
|
| 37 |
+
| Content.Character c => c
|
| 38 |
+
|
| 39 |
+
end
|
| 40 |
+
instance : ToString Element := β¨eToStringβ©
|
| 41 |
+
instance : ToString Content := β¨cToStringβ©
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Data/Xml/Parser.lean
ADDED
|
@@ -0,0 +1,487 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Author: Dany Fabian
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Std.Internal.Parsec
|
| 8 |
+
import Lean.Data.Xml.Basic
|
| 9 |
+
|
| 10 |
+
open System
|
| 11 |
+
open Lean
|
| 12 |
+
|
| 13 |
+
namespace Lean
|
| 14 |
+
namespace Xml
|
| 15 |
+
|
| 16 |
+
open Std.Internal.Parsec
|
| 17 |
+
open Std.Internal.Parsec.String
|
| 18 |
+
|
| 19 |
+
namespace Parser
|
| 20 |
+
|
| 21 |
+
abbrev LeanChar := Char
|
| 22 |
+
|
| 23 |
+
/-- consume a newline character sequence pretending, that we read '\n'. As per spec:
|
| 24 |
+
https://www.w3.org/TR/xml/#sec-line-ends -/
|
| 25 |
+
def endl : Parser LeanChar := (skipString "\r\n" <|> skipChar '\r' <|> skipChar '\n') *> pure '\n'
|
| 26 |
+
|
| 27 |
+
def quote (p : Parser Ξ±) : Parser Ξ± :=
|
| 28 |
+
skipChar '\'' *> p <* skipChar '\''
|
| 29 |
+
<|> skipChar '"' *> p <* skipChar '"'
|
| 30 |
+
|
| 31 |
+
/-- https://www.w3.org/TR/xml/#NT-Char -/
|
| 32 |
+
def Char : Parser LeanChar :=
|
| 33 |
+
(attempt do
|
| 34 |
+
let c β any
|
| 35 |
+
let cNat := c.toNat
|
| 36 |
+
if (0x20 β€ cNat β§ cNat β€ 0xD7FF)
|
| 37 |
+
β¨ (0xE000 β€ cNat β§ cNat β€ 0xFFFD)
|
| 38 |
+
β¨ (0x10000 β€ cNat β§ cNat β€ 0x10FFFF) then pure c else fail "expected xml char")
|
| 39 |
+
<|> pchar '\t' <|> endl
|
| 40 |
+
|
| 41 |
+
/-- https://www.w3.org/TR/xml/#NT-S -/
|
| 42 |
+
def S : Parser String :=
|
| 43 |
+
many1Chars (pchar ' ' <|> endl <|> pchar '\t')
|
| 44 |
+
|
| 45 |
+
/-- https://www.w3.org/TR/xml/#NT-Eq -/
|
| 46 |
+
def Eq : Parser Unit :=
|
| 47 |
+
optional S *> skipChar '=' <* optional S
|
| 48 |
+
|
| 49 |
+
private def nameStartCharRanges : Array (Nat Γ Nat) :=
|
| 50 |
+
#[(0xC0, 0xD6),
|
| 51 |
+
(0xD8, 0xF6),
|
| 52 |
+
(0xF8, 0x2FF),
|
| 53 |
+
(0x370, 0x37D),
|
| 54 |
+
(0x37F, 0x1FFF),
|
| 55 |
+
(0x200C, 0x200D),
|
| 56 |
+
(0x2070, 0x218F),
|
| 57 |
+
(0x2C00, 0x2FEF),
|
| 58 |
+
(0x3001, 0xD7FF),
|
| 59 |
+
(0xF900, 0xFDCF),
|
| 60 |
+
(0xFDF0, 0xFFFD),
|
| 61 |
+
(0x10000, 0xEFFFF)]
|
| 62 |
+
|
| 63 |
+
/-- https://www.w3.org/TR/xml/#NT-NameStartChar -/
|
| 64 |
+
def NameStartChar : Parser LeanChar := attempt do
|
| 65 |
+
let c β any
|
| 66 |
+
if ('A' β€ c β§ c β€ 'Z') β¨ ('a' β€ c β§ c β€ 'z') then pure c
|
| 67 |
+
else if c = ':' β¨ c = '_' then pure c
|
| 68 |
+
else
|
| 69 |
+
let cNum := c.toNat
|
| 70 |
+
if nameStartCharRanges.any (fun (lo, hi) => lo β€ cNum β§ cNum β€ hi) then pure c
|
| 71 |
+
else fail "expected a name character"
|
| 72 |
+
|
| 73 |
+
/-- https://www.w3.org/TR/xml/#NT-NameChar -/
|
| 74 |
+
def NameChar : Parser LeanChar :=
|
| 75 |
+
NameStartChar <|> digit <|> pchar '-' <|> pchar '.' <|> pchar '\xB7'
|
| 76 |
+
<|> satisfy (Ξ» c => ('\u0300' β€ c β§ c β€ '\u036F') β¨ ('\u203F' β€ c β§ c β€ '\u2040'))
|
| 77 |
+
|
| 78 |
+
/-- https://www.w3.org/TR/xml/#NT-Name -/
|
| 79 |
+
def Name : Parser String := do
|
| 80 |
+
let x β NameStartChar
|
| 81 |
+
manyCharsCore NameChar x.toString
|
| 82 |
+
|
| 83 |
+
/-- https://www.w3.org/TR/xml/#NT-VersionNum -/
|
| 84 |
+
def VersionNum : Parser Unit :=
|
| 85 |
+
skipString "1." <* (many1 digit)
|
| 86 |
+
|
| 87 |
+
/-- https://www.w3.org/TR/xml/#NT-VersionInfo -/
|
| 88 |
+
def VersionInfo : Parser Unit := do
|
| 89 |
+
S *>
|
| 90 |
+
skipString "version"
|
| 91 |
+
Eq
|
| 92 |
+
quote VersionNum
|
| 93 |
+
|
| 94 |
+
/-- https://www.w3.org/TR/xml/#NT-EncName -/
|
| 95 |
+
def EncName : Parser String := do
|
| 96 |
+
let x β asciiLetter
|
| 97 |
+
manyCharsCore (asciiLetter <|> digit <|> pchar '-' <|> pchar '_' <|> pchar '.') x.toString
|
| 98 |
+
|
| 99 |
+
/-- https://www.w3.org/TR/xml/#NT-EncodingDecl -/
|
| 100 |
+
def EncodingDecl : Parser String := do
|
| 101 |
+
S *>
|
| 102 |
+
skipString "encoding"
|
| 103 |
+
Eq
|
| 104 |
+
quote EncName
|
| 105 |
+
|
| 106 |
+
/-- https://www.w3.org/TR/xml/#NT-SDDecl -/
|
| 107 |
+
def SDDecl : Parser String := do
|
| 108 |
+
S *> skipString "standalone" *> Eq *> quote (pstring "yes" <|> pstring "no")
|
| 109 |
+
|
| 110 |
+
/-- https://www.w3.org/TR/xml/#NT-XMLDecl -/
|
| 111 |
+
def XMLdecl : Parser Unit := do
|
| 112 |
+
skipString "<?xml"
|
| 113 |
+
VersionInfo
|
| 114 |
+
optional EncodingDecl *>
|
| 115 |
+
optional SDDecl *>
|
| 116 |
+
optional S *>
|
| 117 |
+
skipString "?>"
|
| 118 |
+
|
| 119 |
+
/-- https://www.w3.org/TR/xml/#NT-Comment -/
|
| 120 |
+
def Comment : Parser String :=
|
| 121 |
+
let notDash := Char.toString <$> satisfy (Ξ» c => c β '-')
|
| 122 |
+
skipString "<!--" *>
|
| 123 |
+
Array.foldl String.append "" <$> many (attempt <| notDash <|> (do
|
| 124 |
+
let d β pchar '-'
|
| 125 |
+
let c β notDash
|
| 126 |
+
pure $ d.toString ++ c))
|
| 127 |
+
<* skipString "-->"
|
| 128 |
+
|
| 129 |
+
/-- https://www.w3.org/TR/xml/#NT-PITarget -/
|
| 130 |
+
def PITarget : Parser String :=
|
| 131 |
+
Name <* (skipChar 'X' <|> skipChar 'x') <* (skipChar 'M' <|> skipChar 'm') <* (skipChar 'L' <|> skipChar 'l')
|
| 132 |
+
|
| 133 |
+
/-- https://www.w3.org/TR/xml/#NT-PI -/
|
| 134 |
+
def PI : Parser Unit := do
|
| 135 |
+
skipString "<?"
|
| 136 |
+
<* PITarget <*
|
| 137 |
+
optional (S *> manyChars (notFollowedBy (skipString "?>") *> Char))
|
| 138 |
+
skipString "?>"
|
| 139 |
+
|
| 140 |
+
/-- https://www.w3.org/TR/xml/#NT-Misc -/
|
| 141 |
+
def Misc : Parser Unit :=
|
| 142 |
+
Comment *> pure () <|> PI <|> S *> pure ()
|
| 143 |
+
|
| 144 |
+
/-- https://www.w3.org/TR/xml/#NT-SystemLiteral -/
|
| 145 |
+
def SystemLiteral : Parser String :=
|
| 146 |
+
pchar '"' *> manyChars (satisfy Ξ» c => c β '"') <* pchar '"'
|
| 147 |
+
<|> pchar '\'' *> manyChars (satisfy Ξ» c => c β '\'') <* pure '\''
|
| 148 |
+
|
| 149 |
+
/-- https://www.w3.org/TR/xml/#NT-PubidChar -/
|
| 150 |
+
def PubidChar : Parser LeanChar :=
|
| 151 |
+
asciiLetter <|> digit <|> endl <|> attempt do
|
| 152 |
+
let c β any
|
| 153 |
+
if "-'()+,./:=?;!*#@$_%".contains c then pure c else fail "PublidChar expected"
|
| 154 |
+
|
| 155 |
+
/-- https://www.w3.org/TR/xml/#NT-PubidLiteral -/
|
| 156 |
+
def PubidLiteral : Parser String :=
|
| 157 |
+
pchar '"' *> manyChars PubidChar <* pchar '"'
|
| 158 |
+
<|> pchar '\'' *> manyChars (attempt do
|
| 159 |
+
let c β PubidChar
|
| 160 |
+
if c = '\'' then fail "'\\'' not expected" else pure c) <* pchar '\''
|
| 161 |
+
|
| 162 |
+
/-- https://www.w3.org/TR/xml/#NT-ExternalID -/
|
| 163 |
+
def ExternalID : Parser Unit :=
|
| 164 |
+
skipString "SYSTEM" *> S *> SystemLiteral *> pure ()
|
| 165 |
+
<|> skipString "PUBLIC" *> S *> PubidLiteral *> S *> SystemLiteral *> pure ()
|
| 166 |
+
|
| 167 |
+
/-- https://www.w3.org/TR/xml/#NT-Mixed -/
|
| 168 |
+
def Mixed : Parser Unit :=
|
| 169 |
+
(do
|
| 170 |
+
skipChar '('
|
| 171 |
+
optional S *>
|
| 172 |
+
skipString "#PCDATA" *>
|
| 173 |
+
many (optional S *> skipChar '|' *> optional S *> Name) *>
|
| 174 |
+
optional S *>
|
| 175 |
+
skipString ")*")
|
| 176 |
+
<|> skipChar '(' *> (optional S) *> skipString "#PCDATA" <* (optional S) <* skipChar ')'
|
| 177 |
+
|
| 178 |
+
mutual
|
| 179 |
+
/-- https://www.w3.org/TR/xml/#NT-cp -/
|
| 180 |
+
partial def cp : Parser Unit :=
|
| 181 |
+
(Name *> pure () <|> choice <|> seq) <* optional (skipChar '?' <|> skipChar '*' <|> skipChar '+')
|
| 182 |
+
|
| 183 |
+
/-- https://www.w3.org/TR/xml/#NT-choice -/
|
| 184 |
+
partial def choice : Parser Unit := do
|
| 185 |
+
skipChar '('
|
| 186 |
+
optional S *>
|
| 187 |
+
cp
|
| 188 |
+
many1 (optional S *> skipChar '|' *> optional S *> cp) *>
|
| 189 |
+
optional S *>
|
| 190 |
+
skipChar ')'
|
| 191 |
+
|
| 192 |
+
/-- https://www.w3.org/TR/xml/#NT-seq -/
|
| 193 |
+
partial def seq : Parser Unit := do
|
| 194 |
+
skipChar '('
|
| 195 |
+
optional S *>
|
| 196 |
+
cp
|
| 197 |
+
many (optional S *> skipChar ',' *> optional S *> cp) *>
|
| 198 |
+
optional S *>
|
| 199 |
+
skipChar ')'
|
| 200 |
+
end
|
| 201 |
+
|
| 202 |
+
/-- https://www.w3.org/TR/xml/#NT-children -/
|
| 203 |
+
def children : Parser Unit :=
|
| 204 |
+
(choice <|> seq) <* optional (skipChar '?' <|> skipChar '*' <|> skipChar '+')
|
| 205 |
+
|
| 206 |
+
/-- https://www.w3.org/TR/xml/#NT-contentspec -/
|
| 207 |
+
def contentspec : Parser Unit := do
|
| 208 |
+
skipString "EMPTY" <|> skipString "ANY" <|> Mixed <|> children
|
| 209 |
+
|
| 210 |
+
/-- https://www.w3.org/TR/xml/#NT-elementdecl -/
|
| 211 |
+
def elementDecl : Parser Unit := do
|
| 212 |
+
skipString "<!ELEMENT"
|
| 213 |
+
S *>
|
| 214 |
+
Name *>
|
| 215 |
+
contentspec *>
|
| 216 |
+
optional S *>
|
| 217 |
+
skipChar '>'
|
| 218 |
+
|
| 219 |
+
/-- https://www.w3.org/TR/xml/#NT-StringType -/
|
| 220 |
+
def StringType : Parser Unit :=
|
| 221 |
+
skipString "CDATA"
|
| 222 |
+
|
| 223 |
+
/-- https://www.w3.org/TR/xml/#NT-TokenizedType -/
|
| 224 |
+
def TokenizedType : Parser Unit :=
|
| 225 |
+
skipString "ID"
|
| 226 |
+
<|> skipString "IDREF"
|
| 227 |
+
<|> skipString "IDREFS"
|
| 228 |
+
<|> skipString "ENTITY"
|
| 229 |
+
<|> skipString "ENTITIES"
|
| 230 |
+
<|> skipString "NMTOKEN"
|
| 231 |
+
<|> skipString "NMTOKENS"
|
| 232 |
+
|
| 233 |
+
/-- https://www.w3.org/TR/xml/#NT-NotationType -/
|
| 234 |
+
def NotationType : Parser Unit := do
|
| 235 |
+
skipString "NOTATION"
|
| 236 |
+
S *>
|
| 237 |
+
skipChar '(' <*
|
| 238 |
+
optional S
|
| 239 |
+
Name *> many (optional S *> skipChar '|' *> optional S *> Name) *>
|
| 240 |
+
optional S *>
|
| 241 |
+
skipChar ')'
|
| 242 |
+
|
| 243 |
+
/-- https://www.w3.org/TR/xml/#NT-Nmtoken -/
|
| 244 |
+
def Nmtoken : Parser String := do
|
| 245 |
+
many1Chars NameChar
|
| 246 |
+
|
| 247 |
+
/-- https://www.w3.org/TR/xml/#NT-Enumeration -/
|
| 248 |
+
def Enumeration : Parser Unit := do
|
| 249 |
+
skipChar '('
|
| 250 |
+
optional S *>
|
| 251 |
+
Nmtoken *> many (optional S *> skipChar '|' *> optional S *> Nmtoken) *>
|
| 252 |
+
optional S *>
|
| 253 |
+
skipChar ')'
|
| 254 |
+
|
| 255 |
+
/-- https://www.w3.org/TR/xml/#NT-EnumeratedType -/
|
| 256 |
+
def EnumeratedType : Parser Unit :=
|
| 257 |
+
NotationType <|> Enumeration
|
| 258 |
+
|
| 259 |
+
/-- https://www.w3.org/TR/xml/#NT-AttType -/
|
| 260 |
+
def AttType : Parser Unit :=
|
| 261 |
+
StringType <|> TokenizedType <|> EnumeratedType
|
| 262 |
+
|
| 263 |
+
def predefinedEntityToChar : String β Option LeanChar
|
| 264 |
+
| "lt" => some '<'
|
| 265 |
+
| "gt" => some '>'
|
| 266 |
+
| "amp" => some '&'
|
| 267 |
+
| "apos" => some '\''
|
| 268 |
+
| "quot" => some '"'
|
| 269 |
+
| _ => none
|
| 270 |
+
|
| 271 |
+
/-- https://www.w3.org/TR/xml/#NT-EntityRef -/
|
| 272 |
+
def EntityRef : Parser $ Option LeanChar := attempt $
|
| 273 |
+
skipChar '&' *> predefinedEntityToChar <$> Name <* skipChar ';'
|
| 274 |
+
|
| 275 |
+
@[inline]
|
| 276 |
+
def hexDigitToNat (c : LeanChar) : Nat :=
|
| 277 |
+
if '0' β€ c β§ c β€ '9' then c.toNat - '0'.toNat
|
| 278 |
+
else if 'a' β€ c β§ c β€ 'f' then c.toNat - 'a'.toNat + 10
|
| 279 |
+
else c.toNat - 'A'.toNat + 10
|
| 280 |
+
|
| 281 |
+
def digitsToNat (base : Nat) (digits : Array Nat) : Nat :=
|
| 282 |
+
digits.foldl (Ξ» r d => r * base + d) 0
|
| 283 |
+
|
| 284 |
+
/-- https://www.w3.org/TR/xml/#NT-CharRef -/
|
| 285 |
+
def CharRef : Parser LeanChar := do
|
| 286 |
+
skipString "&#"
|
| 287 |
+
let charCode β
|
| 288 |
+
digitsToNat 10 <$> many1 (hexDigitToNat <$> digit)
|
| 289 |
+
<|> skipChar 'x' *> digitsToNat 16 <$> many1 (hexDigitToNat <$> hexDigit)
|
| 290 |
+
skipChar ';'
|
| 291 |
+
return Char.ofNat charCode
|
| 292 |
+
|
| 293 |
+
/-- https://www.w3.org/TR/xml/#NT-Reference -/
|
| 294 |
+
def Reference : Parser $ Option LeanChar :=
|
| 295 |
+
EntityRef <|> some <$> CharRef
|
| 296 |
+
|
| 297 |
+
/-- https://www.w3.org/TR/xml/#NT-AttValue -/
|
| 298 |
+
def AttValue : Parser String := do
|
| 299 |
+
let chars β
|
| 300 |
+
(do
|
| 301 |
+
skipChar '"'
|
| 302 |
+
many (some <$> satisfy (Ξ» c => c β '<' β§ c β '&' β§ c β '"') <|> Reference) <*
|
| 303 |
+
skipChar '"')
|
| 304 |
+
<|> (do
|
| 305 |
+
skipChar '\''
|
| 306 |
+
many (some <$> satisfy (Ξ» c => c β '<' β§ c β '&' β§ c β '\'') <|> Reference) <*
|
| 307 |
+
skipChar '\'')
|
| 308 |
+
return chars.foldl (Ξ» s c => if let some c := c then s.push c else s) ""
|
| 309 |
+
|
| 310 |
+
/-- https://www.w3.org/TR/xml/#NT-DefaultDecl -/
|
| 311 |
+
def DefaultDecl : Parser Unit :=
|
| 312 |
+
skipString "#REQUIRED"
|
| 313 |
+
<|> skipString "#IMPLIED"
|
| 314 |
+
<|> optional (skipString "#FIXED" <* S) *> AttValue *> pure ()
|
| 315 |
+
|
| 316 |
+
/-- https://www.w3.org/TR/xml/#NT-AttDef -/
|
| 317 |
+
def AttDef : Parser Unit :=
|
| 318 |
+
S *> Name *> S *> AttType *> S *> DefaultDecl
|
| 319 |
+
|
| 320 |
+
/-- https://www.w3.org/TR/xml/#NT-AttlistDecl -/
|
| 321 |
+
def AttlistDecl : Parser Unit :=
|
| 322 |
+
skipString "<!ATTLIST" *> S *> Name *> many AttDef *> optional S *> skipChar '>'
|
| 323 |
+
|
| 324 |
+
/-- https://www.w3.org/TR/xml/#NT-PEReference -/
|
| 325 |
+
def PEReference : Parser Unit :=
|
| 326 |
+
skipChar '%' *> Name *> skipChar ';'
|
| 327 |
+
|
| 328 |
+
/-- https://www.w3.org/TR/xml/#NT-EntityValue -/
|
| 329 |
+
def EntityValue : Parser String := do
|
| 330 |
+
let chars β
|
| 331 |
+
(do
|
| 332 |
+
skipChar '"'
|
| 333 |
+
many (some <$> satisfy (Ξ» c => c β '%' β§ c β '&' β§ c β '"') <|> PEReference *> pure none <|> Reference) <*
|
| 334 |
+
skipChar '"')
|
| 335 |
+
<|> (do
|
| 336 |
+
skipChar '\''
|
| 337 |
+
many (some <$> satisfy (Ξ» c => c β '%' β§ c β '&' β§ c β '\'') <|> PEReference *> pure none <|> Reference) <*
|
| 338 |
+
skipChar '\'')
|
| 339 |
+
return chars.foldl (Ξ» s c => if let some c := c then s.push c else s) ""
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
/-- https://www.w3.org/TR/xml/#NT-NDataDecl -/
|
| 343 |
+
def NDataDecl : Parser Unit :=
|
| 344 |
+
S *> skipString "NDATA" <* S <* Name
|
| 345 |
+
|
| 346 |
+
/-- https://www.w3.org/TR/xml/#NT-EntityDef -/
|
| 347 |
+
def EntityDef : Parser Unit :=
|
| 348 |
+
EntityValue *> pure () <|> (ExternalID <* optional NDataDecl)
|
| 349 |
+
|
| 350 |
+
/-- https://www.w3.org/TR/xml/#NT-GEDecl -/
|
| 351 |
+
def GEDecl : Parser Unit :=
|
| 352 |
+
skipString "<!ENTITY" *> S *> Name *> S *> EntityDef *> optional S *> skipChar '>'
|
| 353 |
+
|
| 354 |
+
/-- https://www.w3.org/TR/xml/#NT-PEDef -/
|
| 355 |
+
def PEDef : Parser Unit :=
|
| 356 |
+
EntityValue *> pure () <|> ExternalID
|
| 357 |
+
|
| 358 |
+
/-- https://www.w3.org/TR/xml/#NT-PEDecl -/
|
| 359 |
+
def PEDecl : Parser Unit :=
|
| 360 |
+
skipString "<!ENTITY" *> S *> skipChar '%' *> S *> Name *> PEDef *> optional S *> skipChar '>'
|
| 361 |
+
|
| 362 |
+
/-- https://www.w3.org/TR/xml/#NT-EntityDecl -/
|
| 363 |
+
def EntityDecl : Parser Unit :=
|
| 364 |
+
GEDecl <|> PEDecl
|
| 365 |
+
|
| 366 |
+
/-- https://www.w3.org/TR/xml/#NT-PublicID -/
|
| 367 |
+
def PublicID : Parser Unit :=
|
| 368 |
+
skipString "PUBLIC" <* S <* PubidLiteral
|
| 369 |
+
|
| 370 |
+
/-- https://www.w3.org/TR/xml/#NT-NotationDecl -/
|
| 371 |
+
def NotationDecl : Parser Unit :=
|
| 372 |
+
skipString "<!NOTATION" *> S *> Name *> (ExternalID <|> PublicID) *> optional S *> skipChar '>'
|
| 373 |
+
|
| 374 |
+
/-- https://www.w3.org/TR/xml/#NT-markupdecl -/
|
| 375 |
+
def markupDecl : Parser Unit :=
|
| 376 |
+
elementDecl <|> AttlistDecl <|> EntityDecl <|> NotationDecl <|> PI <|> (Comment *> pure ())
|
| 377 |
+
|
| 378 |
+
/-- https://www.w3.org/TR/xml/#NT-DeclSep -/
|
| 379 |
+
def DeclSep : Parser Unit :=
|
| 380 |
+
PEReference <|> S *> pure ()
|
| 381 |
+
|
| 382 |
+
/-- https://www.w3.org/TR/xml/#NT-intSubset -/
|
| 383 |
+
def intSubset : Parser Unit :=
|
| 384 |
+
many (markupDecl <|> DeclSep) *> pure ()
|
| 385 |
+
|
| 386 |
+
/-- https://www.w3.org/TR/xml/#NT-doctypedecl -/
|
| 387 |
+
def doctypedecl : Parser Unit := do
|
| 388 |
+
skipString "<!DOCTYPE"
|
| 389 |
+
S *>
|
| 390 |
+
Name *>
|
| 391 |
+
optional (S *> ExternalID) *> pure ()
|
| 392 |
+
<* optional S
|
| 393 |
+
optional (skipChar '[' *> intSubset <* skipChar ']' <* optional S) *>
|
| 394 |
+
skipChar '>'
|
| 395 |
+
|
| 396 |
+
/-- https://www.w3.org/TR/xml/#NT-prolog -/
|
| 397 |
+
def prolog : Parser Unit :=
|
| 398 |
+
optional XMLdecl *>
|
| 399 |
+
many Misc *>
|
| 400 |
+
optional (doctypedecl <* many Misc) *> pure ()
|
| 401 |
+
|
| 402 |
+
/-- https://www.w3.org/TR/xml/#NT-Attribute -/
|
| 403 |
+
def Attribute : Parser (String Γ String) := do
|
| 404 |
+
let name β Name
|
| 405 |
+
Eq
|
| 406 |
+
let value β AttValue
|
| 407 |
+
return (name, value)
|
| 408 |
+
|
| 409 |
+
protected def elementPrefix : Parser (Array Content β Element) := do
|
| 410 |
+
skipChar '<'
|
| 411 |
+
let name β Name
|
| 412 |
+
let attributes β many (attempt <| S *> Attribute)
|
| 413 |
+
optional S *> pure ()
|
| 414 |
+
return Element.Element name (RBMap.fromList attributes.toList compare)
|
| 415 |
+
|
| 416 |
+
/-- https://www.w3.org/TR/xml/#NT-EmptyElemTag -/
|
| 417 |
+
def EmptyElemTag (elem : Array Content β Element) : Parser Element := do
|
| 418 |
+
skipString "/>" *> pure (elem #[])
|
| 419 |
+
|
| 420 |
+
/-- https://www.w3.org/TR/xml/#NT-STag -/
|
| 421 |
+
def STag (elem : Array Content β Element) : Parser (Array Content β Element) := do
|
| 422 |
+
skipChar '>' *> pure elem
|
| 423 |
+
|
| 424 |
+
/-- https://www.w3.org/TR/xml/#NT-ETag -/
|
| 425 |
+
def ETag : Parser Unit :=
|
| 426 |
+
skipString "</" *> Name *> optional S *> skipChar '>'
|
| 427 |
+
|
| 428 |
+
/-- https://www.w3.org/TR/xml/#NT-CDStart -/
|
| 429 |
+
def CDStart : Parser Unit :=
|
| 430 |
+
skipString "<![CDATA["
|
| 431 |
+
|
| 432 |
+
/-- https://www.w3.org/TR/xml/#NT-CDEnd -/
|
| 433 |
+
def CDEnd : Parser Unit :=
|
| 434 |
+
skipString "]]>"
|
| 435 |
+
|
| 436 |
+
/-- https://www.w3.org/TR/xml/#NT-CData -/
|
| 437 |
+
def CData : Parser String :=
|
| 438 |
+
manyChars (notFollowedBy (skipString "]]>") *> any)
|
| 439 |
+
|
| 440 |
+
/-- https://www.w3.org/TR/xml/#NT-CDSect -/
|
| 441 |
+
def CDSect : Parser String :=
|
| 442 |
+
CDStart *> CData <* CDEnd
|
| 443 |
+
|
| 444 |
+
/-- https://www.w3.org/TR/xml/#NT-CharData -/
|
| 445 |
+
def CharData : Parser String :=
|
| 446 |
+
notFollowedBy (skipString "]]>") *> manyChars (satisfy Ξ» c => c β '<' β§ c β '&')
|
| 447 |
+
|
| 448 |
+
mutual
|
| 449 |
+
/-- https://www.w3.org/TR/xml/#NT-content -/
|
| 450 |
+
partial def content : Parser (Array Content) := do
|
| 451 |
+
let x β optional (Content.Character <$> CharData)
|
| 452 |
+
let xs β many do
|
| 453 |
+
let y β
|
| 454 |
+
attempt (some <$> Content.Element <$> element)
|
| 455 |
+
<|> (do let c β Reference; pure <| c.map (Content.Character β Char.toString))
|
| 456 |
+
<|> some <$> Content.Character <$> CDSect
|
| 457 |
+
<|> PI *> pure none
|
| 458 |
+
<|> some <$> Content.Comment <$> Comment
|
| 459 |
+
|
| 460 |
+
let z β optional (Content.Character <$> CharData)
|
| 461 |
+
pure #[y, z]
|
| 462 |
+
let xs := #[x] ++ xs.flatMap id |>.filterMap id
|
| 463 |
+
let mut res := #[]
|
| 464 |
+
for x in xs do
|
| 465 |
+
if res.size > 0 then
|
| 466 |
+
match res.back!, x with
|
| 467 |
+
| Content.Character x, Content.Character y => res := res.set! (res.size - 1) (Content.Character $ x ++ y)
|
| 468 |
+
| _, x => res := res.push x
|
| 469 |
+
else res := res.push x
|
| 470 |
+
return res
|
| 471 |
+
|
| 472 |
+
/-- https://www.w3.org/TR/xml/#NT-element -/
|
| 473 |
+
partial def element : Parser Element := do
|
| 474 |
+
let elem β Parser.elementPrefix
|
| 475 |
+
EmptyElemTag elem <|> STag elem <*> content <* ETag
|
| 476 |
+
|
| 477 |
+
end
|
| 478 |
+
|
| 479 |
+
/-- https://www.w3.org/TR/xml/#NT-document -/
|
| 480 |
+
def document : Parser Element := prolog *> element <* many Misc <* eof
|
| 481 |
+
|
| 482 |
+
end Parser
|
| 483 |
+
|
| 484 |
+
def parse (s : String) : Except String Element :=
|
| 485 |
+
Parser.run Xml.Parser.document s
|
| 486 |
+
|
| 487 |
+
end Xml
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/DocString/Add.lean
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: David Thrane Christiansen
|
| 5 |
+
-/
|
| 6 |
+
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Environment
|
| 9 |
+
import Lean.Exception
|
| 10 |
+
import Lean.Log
|
| 11 |
+
import Lean.DocString.Extension
|
| 12 |
+
import Lean.DocString.Links
|
| 13 |
+
|
| 14 |
+
set_option linter.missingDocs true
|
| 15 |
+
|
| 16 |
+
namespace Lean
|
| 17 |
+
|
| 18 |
+
/--
|
| 19 |
+
Validates all links to the Lean reference manual in `docstring`.
|
| 20 |
+
|
| 21 |
+
This is intended to be used before saving a docstring that is later subject to rewriting with
|
| 22 |
+
`rewriteManualLinks`.
|
| 23 |
+
-/
|
| 24 |
+
def validateDocComment
|
| 25 |
+
[Monad m] [MonadLog m] [AddMessageContext m] [MonadOptions m] [MonadLiftT IO m]
|
| 26 |
+
(docstring : TSyntax `Lean.Parser.Command.docComment) :
|
| 27 |
+
m Unit := do
|
| 28 |
+
let str := docstring.getDocString
|
| 29 |
+
let pos? := docstring.raw[1].getHeadInfo? >>= (Β·.getPos?)
|
| 30 |
+
|
| 31 |
+
let (errs, out) β (rewriteManualLinksCore str : IO _)
|
| 32 |
+
|
| 33 |
+
for (β¨start, stopβ©, err) in errs do
|
| 34 |
+
-- Report errors at their actual location if possible
|
| 35 |
+
if let some pos := pos? then
|
| 36 |
+
let urlStx : Syntax := .atom (.synthetic (pos + start) (pos + stop)) (str.extract start stop)
|
| 37 |
+
logErrorAt urlStx err
|
| 38 |
+
else
|
| 39 |
+
logError err
|
| 40 |
+
|
| 41 |
+
/--
|
| 42 |
+
Adds a docstring to the environment, validating documentation links.
|
| 43 |
+
-/
|
| 44 |
+
def addDocString
|
| 45 |
+
[Monad m] [MonadError m] [MonadEnv m] [MonadLog m] [AddMessageContext m] [MonadOptions m] [MonadLiftT IO m]
|
| 46 |
+
(declName : Name) (docComment : TSyntax `Lean.Parser.Command.docComment) : m Unit := do
|
| 47 |
+
unless (β getEnv).getModuleIdxFor? declName |>.isNone do
|
| 48 |
+
throwError s!"invalid doc string, declaration '{declName}' is in an imported module"
|
| 49 |
+
validateDocComment docComment
|
| 50 |
+
let docString : String β getDocStringText docComment
|
| 51 |
+
modifyEnv fun env => docStringExt.insert env declName docString.removeLeadingSpaces
|
| 52 |
+
|
| 53 |
+
/--
|
| 54 |
+
Adds a docstring to the environment, validating documentation links.
|
| 55 |
+
-/
|
| 56 |
+
def addDocString'
|
| 57 |
+
[Monad m] [MonadError m] [MonadEnv m] [MonadLog m] [AddMessageContext m] [MonadOptions m] [MonadLiftT IO m]
|
| 58 |
+
(declName : Name) (docString? : Option (TSyntax `Lean.Parser.Command.docComment)) : m Unit :=
|
| 59 |
+
match docString? with
|
| 60 |
+
| some docString => addDocString declName docString
|
| 61 |
+
| none => return ()
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/DocString/Extension.lean
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.DeclarationRange
|
| 8 |
+
import Lean.DocString.Links
|
| 9 |
+
import Lean.MonadEnv
|
| 10 |
+
import Init.Data.String.Extra
|
| 11 |
+
|
| 12 |
+
-- This module contains the underlying data for docstrings, with as few imports as possible, so that
|
| 13 |
+
-- docstrings can be saved in as much of the compiler as possible.
|
| 14 |
+
-- The module `Lean.DocString` contains the query interface, which needs to look at additional data
|
| 15 |
+
-- to construct user-visible docstrings.
|
| 16 |
+
|
| 17 |
+
namespace Lean
|
| 18 |
+
|
| 19 |
+
private builtin_initialize builtinDocStrings : IO.Ref (NameMap String) β IO.mkRef {}
|
| 20 |
+
builtin_initialize docStringExt : MapDeclarationExtension String β mkMapDeclarationExtension
|
| 21 |
+
|
| 22 |
+
/--
|
| 23 |
+
Adds a builtin docstring to the compiler.
|
| 24 |
+
|
| 25 |
+
Links to the Lean manual aren't validated.
|
| 26 |
+
-/
|
| 27 |
+
-- See the test `lean/run/docstringRewrites.lean` for the validation of builtin docstring links
|
| 28 |
+
def addBuiltinDocString (declName : Name) (docString : String) : IO Unit := do
|
| 29 |
+
builtinDocStrings.modify (Β·.insert declName docString.removeLeadingSpaces)
|
| 30 |
+
|
| 31 |
+
def addDocStringCore [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString : String) : m Unit := do
|
| 32 |
+
unless (β getEnv).getModuleIdxFor? declName |>.isNone do
|
| 33 |
+
throwError s!"invalid doc string, declaration '{declName}' is in an imported module"
|
| 34 |
+
modifyEnv fun env => docStringExt.insert env declName docString.removeLeadingSpaces
|
| 35 |
+
|
| 36 |
+
def addDocStringCore' [Monad m] [MonadError m] [MonadEnv m] (declName : Name) (docString? : Option String) : m Unit :=
|
| 37 |
+
match docString? with
|
| 38 |
+
| some docString => addDocStringCore declName docString
|
| 39 |
+
| none => return ()
|
| 40 |
+
|
| 41 |
+
/--
|
| 42 |
+
Finds a docstring without performing any alias resolution or enrichment with extra metadata.
|
| 43 |
+
|
| 44 |
+
Docstrings to be shown to a user should be looked up with `Lean.findDocString?` instead.
|
| 45 |
+
-/
|
| 46 |
+
def findSimpleDocString? (env : Environment) (declName : Name) (includeBuiltin := true) : IO (Option String) :=
|
| 47 |
+
if let some docStr := docStringExt.find? env declName then
|
| 48 |
+
return some docStr
|
| 49 |
+
else if includeBuiltin then
|
| 50 |
+
return (β builtinDocStrings.get).find? declName
|
| 51 |
+
else
|
| 52 |
+
return none
|
| 53 |
+
|
| 54 |
+
structure ModuleDoc where
|
| 55 |
+
doc : String
|
| 56 |
+
declarationRange : DeclarationRange
|
| 57 |
+
|
| 58 |
+
private builtin_initialize moduleDocExt : SimplePersistentEnvExtension ModuleDoc (PersistentArray ModuleDoc) β registerSimplePersistentEnvExtension {
|
| 59 |
+
addImportedFn := fun _ => {}
|
| 60 |
+
addEntryFn := fun s e => s.push e
|
| 61 |
+
exportEntriesFnEx? := some fun _ _ es level =>
|
| 62 |
+
if level < .server then
|
| 63 |
+
#[]
|
| 64 |
+
else
|
| 65 |
+
es.toArray
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
def addMainModuleDoc (env : Environment) (doc : ModuleDoc) : Environment :=
|
| 69 |
+
moduleDocExt.addEntry env doc
|
| 70 |
+
|
| 71 |
+
def getMainModuleDoc (env : Environment) : PersistentArray ModuleDoc :=
|
| 72 |
+
moduleDocExt.getState env
|
| 73 |
+
|
| 74 |
+
def getModuleDoc? (env : Environment) (moduleName : Name) : Option (Array ModuleDoc) :=
|
| 75 |
+
env.getModuleIdx? moduleName |>.map fun modIdx =>
|
| 76 |
+
moduleDocExt.getModuleEntries (level := .server) env modIdx
|
| 77 |
+
|
| 78 |
+
def getDocStringText [Monad m] [MonadError m] (stx : TSyntax `Lean.Parser.Command.docComment) : m String :=
|
| 79 |
+
match stx.raw[1] with
|
| 80 |
+
| Syntax.atom _ val => return val.extract 0 (val.endPos - β¨2β©)
|
| 81 |
+
| _ => throwErrorAt stx "unexpected doc string{indentD stx.raw[1]}"
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/DocString/Links.lean
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: David Thrane Christiansen
|
| 5 |
+
-/
|
| 6 |
+
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Syntax
|
| 9 |
+
|
| 10 |
+
set_option linter.missingDocs true
|
| 11 |
+
|
| 12 |
+
namespace Lean
|
| 13 |
+
|
| 14 |
+
@[extern "lean_manual_get_root"]
|
| 15 |
+
private opaque getManualRoot : Unit β String
|
| 16 |
+
|
| 17 |
+
private def fallbackManualRoot := "https://lean-lang.org/doc/reference/latest/"
|
| 18 |
+
|
| 19 |
+
/--
|
| 20 |
+
Computes the root of the Lean reference manual that should be used for targets.
|
| 21 |
+
|
| 22 |
+
If the environment variable `LEAN_MANUAL_ROOT` is set, it is used as the root. If not, but a manual
|
| 23 |
+
root is pre-configured for the current Lean executable (typically true for releases), then it is
|
| 24 |
+
used. If neither are true, then `https://lean-lang.org/doc/reference/latest/` is used.
|
| 25 |
+
-/
|
| 26 |
+
builtin_initialize manualRoot : String β
|
| 27 |
+
let r β
|
| 28 |
+
if let some root := (β IO.getEnv "LEAN_MANUAL_ROOT") then
|
| 29 |
+
pure root
|
| 30 |
+
else
|
| 31 |
+
let root := getManualRoot ()
|
| 32 |
+
if root.isEmpty then
|
| 33 |
+
pure fallbackManualRoot
|
| 34 |
+
else
|
| 35 |
+
pure root
|
| 36 |
+
return if r.endsWith "/" then r else r ++ "/"
|
| 37 |
+
|
| 38 |
+
/--
|
| 39 |
+
The manual domain for error explanations.
|
| 40 |
+
|
| 41 |
+
We expose this because it is used to populate the URL of the error message description widget.
|
| 42 |
+
-/
|
| 43 |
+
def errorExplanationManualDomain :=
|
| 44 |
+
"Manual.errorExplanation"
|
| 45 |
+
|
| 46 |
+
-- TODO: we may wish to make this more general for domains that require additional arguments
|
| 47 |
+
/-- Maps `lean-manual` URL paths to their corresponding manual domains. -/
|
| 48 |
+
private def domainMap : Std.HashMap String String :=
|
| 49 |
+
Std.HashMap.ofList [
|
| 50 |
+
("section", "Verso.Genre.Manual.section"),
|
| 51 |
+
("errorExplanation", errorExplanationManualDomain)
|
| 52 |
+
]
|
| 53 |
+
|
| 54 |
+
/--
|
| 55 |
+
Rewrites links from the internal Lean manual syntax to the correct URL. This rewriting is an
|
| 56 |
+
overapproximation: any parentheses containing the internal syntax of a Lean manual URL is rewritten.
|
| 57 |
+
|
| 58 |
+
The internal syntax is the URL scheme `lean-manual` followed by the path `/KIND/MORE...`, where
|
| 59 |
+
`KIND` is a kind of content being linked to. Presently, the only valid kind is `section`, and it
|
| 60 |
+
requires that the remainder of the path consists of one element, which is a section or part identifier.
|
| 61 |
+
|
| 62 |
+
The correct URL is based on a manual root URL, which is determined by the `LEAN_MANUAL_ROOT`
|
| 63 |
+
environment variable. If this environment variable is not set, a manual root provided when Lean was
|
| 64 |
+
built is used (typically this is the version corresponding to the current release). If no such root
|
| 65 |
+
is available, the latest version of the manual is used.
|
| 66 |
+
-/
|
| 67 |
+
def rewriteManualLinksCore (s : String) : BaseIO (Array (String.Range Γ String) Γ String) := do
|
| 68 |
+
let scheme := "lean-manual://"
|
| 69 |
+
let mut out := ""
|
| 70 |
+
let mut errors := #[]
|
| 71 |
+
let mut iter := s.iter
|
| 72 |
+
while h : iter.hasNext do
|
| 73 |
+
let c := iter.curr' h
|
| 74 |
+
iter := iter.next' h
|
| 75 |
+
|
| 76 |
+
if !lookingAt scheme iter.prev then
|
| 77 |
+
out := out.push c
|
| 78 |
+
continue
|
| 79 |
+
|
| 80 |
+
let start := iter.prev.forward scheme.length
|
| 81 |
+
let mut iter' := start
|
| 82 |
+
while h' : iter'.hasNext do
|
| 83 |
+
let c' := iter'.curr' h'
|
| 84 |
+
iter' := iter'.next' h'
|
| 85 |
+
if urlChar c' && !iter'.atEnd then
|
| 86 |
+
continue
|
| 87 |
+
match rw (start.extract iter'.prev) with
|
| 88 |
+
| .error err =>
|
| 89 |
+
errors := errors.push (β¨iter.prev.i, iter'.prev.iβ©, err)
|
| 90 |
+
out := out.push c
|
| 91 |
+
break
|
| 92 |
+
| .ok path =>
|
| 93 |
+
out := out ++ manualRoot ++ path
|
| 94 |
+
out := out.push c'
|
| 95 |
+
iter := iter'
|
| 96 |
+
break
|
| 97 |
+
|
| 98 |
+
pure (errors, out)
|
| 99 |
+
|
| 100 |
+
where
|
| 101 |
+
/--
|
| 102 |
+
Returns `true` if the character is one of those allowed in RFC 3986 sections 2.2 and 2.3. other
|
| 103 |
+
than '(' or')'.
|
| 104 |
+
-/
|
| 105 |
+
urlChar (c : Char) : Bool :=
|
| 106 |
+
-- unreserved
|
| 107 |
+
c.isAlphanum || c == '-' || c == '.' || c == '_' || c == '~' ||
|
| 108 |
+
-- gen-delims
|
| 109 |
+
c == ':' || c == '/' || c == '?' || c == '#' || c == '[' || c == ']' || c == '@' ||
|
| 110 |
+
-- sub-delims
|
| 111 |
+
-- ( and ) are excluded due to Markdown's link syntax
|
| 112 |
+
c == '!' || c == '$' || c == '&' || c == '\'' || /- c == '(' || c == ')' || -/ c == '*' ||
|
| 113 |
+
c == '+' || c == ',' || c == ';' || c == '='
|
| 114 |
+
|
| 115 |
+
/--
|
| 116 |
+
Returns `true` if `goal` is a prefix of the string at the position pointed to by `iter`.
|
| 117 |
+
-/
|
| 118 |
+
lookingAt (goal : String) (iter : String.Iterator) : Bool :=
|
| 119 |
+
iter.s.substrEq iter.i goal 0 goal.endPos.byteIdx
|
| 120 |
+
|
| 121 |
+
rw (path : String) : Except String String := do
|
| 122 |
+
match path.splitOn "/" with
|
| 123 |
+
| [] | [""] =>
|
| 124 |
+
throw "Missing documentation type"
|
| 125 |
+
| kind :: args =>
|
| 126 |
+
if let some domain := domainMap.get? kind then
|
| 127 |
+
if let [s] := args then
|
| 128 |
+
if s.isEmpty then
|
| 129 |
+
throw s!"Empty {kind} ID"
|
| 130 |
+
return s!"find/?domain={domain}&name={s}"
|
| 131 |
+
else
|
| 132 |
+
throw s!"Expected one item after `{kind}`, but got {args}"
|
| 133 |
+
else
|
| 134 |
+
let acceptableKinds := ", ".intercalate <| domainMap.toList.map fun (k, _) => s!"`{k}`"
|
| 135 |
+
throw s!"Unknown documentation type `{kind}`. Expected one of the following: {acceptableKinds}"
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
/--
|
| 139 |
+
Rewrites Lean reference manual links in `docstring` to point at the reference manual.
|
| 140 |
+
|
| 141 |
+
This assumes that all such links have already been validated by `validateDocComment`.
|
| 142 |
+
-/
|
| 143 |
+
def rewriteManualLinks (docString : String) : BaseIO String := do
|
| 144 |
+
let (errs, str) β rewriteManualLinksCore docString
|
| 145 |
+
if !errs.isEmpty then
|
| 146 |
+
let errReport :=
|
| 147 |
+
r#"**β Syntax Errors in Lean Language Reference Links**
|
| 148 |
+
|
| 149 |
+
The `lean-manual` URL scheme is used to link to the version of the Lean reference manual that
|
| 150 |
+
corresponds to this version of Lean. Errors occurred while processing the links in this documentation
|
| 151 |
+
comment:
|
| 152 |
+
"# ++
|
| 153 |
+
String.join (errs.toList.map (fun (β¨s, eβ©, msg) => s!" * ```{docString.extract s e}```: {msg}\n\n"))
|
| 154 |
+
return str ++ "\n\n" ++ errReport
|
| 155 |
+
return str
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
/--
|
| 159 |
+
Validates all links to the Lean reference manual in `docstring`, printing an error message if any
|
| 160 |
+
are invalid. Returns `true` if all links are valid.
|
| 161 |
+
|
| 162 |
+
This is intended to be used before saving a docstring that is later subject to rewriting with
|
| 163 |
+
`rewriteManualLinks`, in contexts where the imports needed to produce better error messages in
|
| 164 |
+
`validateDocComment` are not yet available.
|
| 165 |
+
-/
|
| 166 |
+
def validateBuiltinDocString (docString : String) : IO Unit := do
|
| 167 |
+
let (errs, _) β rewriteManualLinksCore docString
|
| 168 |
+
if !errs.isEmpty then
|
| 169 |
+
throw <| IO.userError <|
|
| 170 |
+
s!"Errors in builtin documentation comment:\n" ++
|
| 171 |
+
String.join (errs.toList.map fun (β¨s, eβ©, msg) => s!" * {repr <| docString.extract s e}:\n {msg}\n")
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/App.lean
ADDED
|
@@ -0,0 +1,1854 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Util.FindMVar
|
| 8 |
+
import Lean.Util.CollectFVars
|
| 9 |
+
import Lean.Parser.Term
|
| 10 |
+
import Lean.Meta.KAbstract
|
| 11 |
+
import Lean.Meta.Tactic.ElimInfo
|
| 12 |
+
import Lean.Elab.Term
|
| 13 |
+
import Lean.Elab.Binders
|
| 14 |
+
import Lean.Elab.SyntheticMVars
|
| 15 |
+
import Lean.Elab.Arg
|
| 16 |
+
import Lean.Elab.RecAppSyntax
|
| 17 |
+
|
| 18 |
+
namespace Lean.Elab.Term
|
| 19 |
+
open Meta
|
| 20 |
+
|
| 21 |
+
/--
|
| 22 |
+
Instructs the elaborator to elaborate applications of the given declaration without an expected
|
| 23 |
+
type. This may prevent the elaborator from incorrectly inferring implicit arguments.
|
| 24 |
+
-/
|
| 25 |
+
@[builtin_doc]
|
| 26 |
+
builtin_initialize elabWithoutExpectedTypeAttr : TagAttribute β
|
| 27 |
+
registerTagAttribute `elab_without_expected_type "mark that applications of the given declaration should be elaborated without the expected type"
|
| 28 |
+
|
| 29 |
+
def hasElabWithoutExpectedType (env : Environment) (declName : Name) : Bool :=
|
| 30 |
+
elabWithoutExpectedTypeAttr.hasTag env declName
|
| 31 |
+
|
| 32 |
+
instance : ToString Arg where
|
| 33 |
+
toString
|
| 34 |
+
| .stx val => toString val
|
| 35 |
+
| .expr val => toString val
|
| 36 |
+
|
| 37 |
+
instance : ToString NamedArg where
|
| 38 |
+
toString s := "(" ++ toString s.name ++ " := " ++ toString s.val ++ ")"
|
| 39 |
+
|
| 40 |
+
def throwInvalidNamedArg (namedArg : NamedArg) (fn? : Option Name) : TermElabM Ξ± :=
|
| 41 |
+
withRef namedArg.ref <| match fn? with
|
| 42 |
+
| some fn => throwError "invalid argument name '{namedArg.name}' for function '{fn}'"
|
| 43 |
+
| none => throwError "invalid argument name '{namedArg.name}' for function"
|
| 44 |
+
|
| 45 |
+
private def ensureArgType (f : Expr) (arg : Expr) (expectedType : Expr) : TermElabM Expr := do
|
| 46 |
+
try
|
| 47 |
+
ensureHasType expectedType arg none f
|
| 48 |
+
catch
|
| 49 |
+
| ex@(.error ..) =>
|
| 50 |
+
if (β read).errToSorry then
|
| 51 |
+
exceptionToSorry ex expectedType
|
| 52 |
+
else
|
| 53 |
+
throw ex
|
| 54 |
+
| ex => throw ex
|
| 55 |
+
|
| 56 |
+
private def mkProjAndCheck (structName : Name) (idx : Nat) (e : Expr) : MetaM Expr := do
|
| 57 |
+
let r := mkProj structName idx e
|
| 58 |
+
let eType β inferType e
|
| 59 |
+
if (β isProp eType) then
|
| 60 |
+
let rType β inferType r
|
| 61 |
+
if !(β isProp rType) then
|
| 62 |
+
throwError "Invalid projection: Cannot project a value of non-propositional type{indentExpr rType}\
|
| 63 |
+
\nfrom the expression{indentExpr e}\nwhich has propositional type{indentExpr eType}"
|
| 64 |
+
return r
|
| 65 |
+
|
| 66 |
+
def synthesizeAppInstMVars (instMVars : Array MVarId) (app : Expr) : TermElabM Unit :=
|
| 67 |
+
for mvarId in instMVars do
|
| 68 |
+
unless (β synthesizeInstMVarCore mvarId) do
|
| 69 |
+
registerSyntheticMVarWithCurrRef mvarId (.typeClass none)
|
| 70 |
+
registerMVarErrorImplicitArgInfo mvarId (β getRef) app
|
| 71 |
+
|
| 72 |
+
/-- Return `some namedArg` if `namedArgs` contains an entry for `binderName`. -/
|
| 73 |
+
private def findBinderName? (namedArgs : List NamedArg) (binderName : Name) : Option NamedArg :=
|
| 74 |
+
namedArgs.find? fun namedArg => namedArg.name == binderName
|
| 75 |
+
|
| 76 |
+
/-- Erase entry for `binderName` from `namedArgs`. -/
|
| 77 |
+
def eraseNamedArg (namedArgs : List NamedArg) (binderName : Name) : List NamedArg :=
|
| 78 |
+
namedArgs.filter (Β·.name != binderName)
|
| 79 |
+
|
| 80 |
+
/-- Return true if the given type contains `OptParam` or `AutoParams` -/
|
| 81 |
+
private def hasOptAutoParams (type : Expr) : MetaM Bool := do
|
| 82 |
+
forallTelescopeReducing type fun xs _ =>
|
| 83 |
+
xs.anyM fun x => do
|
| 84 |
+
let xType β inferType x
|
| 85 |
+
return xType.getOptParamDefault?.isSome || xType.getAutoParamTactic?.isSome
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
/-! # Default application elaborator -/
|
| 89 |
+
namespace ElabAppArgs
|
| 90 |
+
|
| 91 |
+
structure Context where
|
| 92 |
+
/--
|
| 93 |
+
`true` if `..` was used
|
| 94 |
+
-/
|
| 95 |
+
ellipsis : Bool --
|
| 96 |
+
/--
|
| 97 |
+
`true` if `@` modifier was used
|
| 98 |
+
-/
|
| 99 |
+
explicit : Bool
|
| 100 |
+
/--
|
| 101 |
+
If the result type of an application is the `outParam` of some local instance, then special support may be needed
|
| 102 |
+
because type class resolution interacts poorly with coercions in this kind of situation.
|
| 103 |
+
This flag enables the special support.
|
| 104 |
+
|
| 105 |
+
The idea is quite simple, if the result type is the `outParam` of some local instance, we simply
|
| 106 |
+
execute `synthesizeSyntheticMVarsUsingDefault`. We added this feature to make sure examples as follows
|
| 107 |
+
are correctly elaborated.
|
| 108 |
+
```lean
|
| 109 |
+
class GetElem (Cont : Type u) (Idx : Type v) (Elem : outParam (Type w)) where
|
| 110 |
+
getElem (xs : Cont) (i : Idx) : Elem
|
| 111 |
+
|
| 112 |
+
export GetElem (getElem)
|
| 113 |
+
|
| 114 |
+
instance : GetElem (Array Ξ±) Nat Ξ± where
|
| 115 |
+
getElem xs i := xs.get β¨i, sorryβ©
|
| 116 |
+
|
| 117 |
+
opaque f : Option Bool β Bool
|
| 118 |
+
opaque g : Bool β Bool
|
| 119 |
+
|
| 120 |
+
def bad (xs : Array Bool) : Bool :=
|
| 121 |
+
let x := getElem xs 0
|
| 122 |
+
f x && g x
|
| 123 |
+
```
|
| 124 |
+
Without the special support, Lean fails at `g x` saying `x` has type `Option Bool` but is expected to have type `Bool`.
|
| 125 |
+
From the user's point of view this is a bug, since `let x := getElem xs 0` clearly constrains `x` to be `Bool`, but
|
| 126 |
+
we only obtain this information after we apply the `OfNat` default instance for `0`.
|
| 127 |
+
|
| 128 |
+
Before converging to this solution, we have tried to create a "coercion placeholder" when `resultIsOutParamSupport = true`,
|
| 129 |
+
but it did not work well in practice. For example, it failed in the example above.
|
| 130 |
+
-/
|
| 131 |
+
resultIsOutParamSupport : Bool
|
| 132 |
+
|
| 133 |
+
/-- Auxiliary structure for elaborating the application `f args namedArgs`. -/
|
| 134 |
+
structure State where
|
| 135 |
+
f : Expr
|
| 136 |
+
fType : Expr
|
| 137 |
+
/-- Remaining regular arguments. -/
|
| 138 |
+
args : List Arg
|
| 139 |
+
/-- remaining named arguments to be processed. -/
|
| 140 |
+
namedArgs : List NamedArg
|
| 141 |
+
expectedType? : Option Expr
|
| 142 |
+
/--
|
| 143 |
+
When named arguments are provided and explicit arguments occurring before them are missing,
|
| 144 |
+
the elaborator eta-expands the declaration. For example,
|
| 145 |
+
```
|
| 146 |
+
def f (x y : Nat) := x + y
|
| 147 |
+
#check f (y := 5)
|
| 148 |
+
-- fun x => f x 5
|
| 149 |
+
```
|
| 150 |
+
`etaArgs` stores the fresh free variables for implementing the eta-expansion.
|
| 151 |
+
When `..` is used, eta-expansion is disabled, and missing arguments are treated as `_`.
|
| 152 |
+
-/
|
| 153 |
+
etaArgs : Array Expr := #[]
|
| 154 |
+
/-- Metavariables that we need to set the error context using the application being built. -/
|
| 155 |
+
toSetErrorCtx : Array MVarId := #[]
|
| 156 |
+
/-- Metavariables for the instance implicit arguments that have already been processed. -/
|
| 157 |
+
instMVars : Array MVarId := #[]
|
| 158 |
+
/--
|
| 159 |
+
The following field is used to implement the `propagateExpectedType` heuristic.
|
| 160 |
+
It is set to `true` true when `expectedType` still has to be propagated.
|
| 161 |
+
-/
|
| 162 |
+
propagateExpected : Bool
|
| 163 |
+
/--
|
| 164 |
+
If the result type may be the `outParam` of some local instance.
|
| 165 |
+
See comment at `Context.resultIsOutParamSupport`
|
| 166 |
+
-/
|
| 167 |
+
resultTypeOutParam? : Option MVarId := none
|
| 168 |
+
|
| 169 |
+
abbrev M := ReaderT Context (StateRefT State TermElabM)
|
| 170 |
+
|
| 171 |
+
/-- Add the given metavariable to the collection of metavariables associated with instance-implicit arguments. -/
|
| 172 |
+
private def addInstMVar (mvarId : MVarId) : M Unit :=
|
| 173 |
+
modify fun s => { s with instMVars := s.instMVars.push mvarId }
|
| 174 |
+
|
| 175 |
+
/--
|
| 176 |
+
Try to synthesize metavariables are `instMVars` using type class resolution.
|
| 177 |
+
The ones that cannot be synthesized yet stay in the `instMVars` list.
|
| 178 |
+
Remark: we use this method
|
| 179 |
+
- before trying to apply coercions to function,
|
| 180 |
+
- before unifying the expected type.
|
| 181 |
+
-/
|
| 182 |
+
def trySynthesizeAppInstMVars : M Unit := do
|
| 183 |
+
let instMVars β (β get).instMVars.filterM fun instMVar => do
|
| 184 |
+
unless (β instantiateMVars (β inferType (.mvar instMVar))).isMVar do try
|
| 185 |
+
if (β synthesizeInstMVarCore instMVar) then
|
| 186 |
+
return false
|
| 187 |
+
catch _ => pure ()
|
| 188 |
+
return true
|
| 189 |
+
modify ({ Β· with instMVars })
|
| 190 |
+
|
| 191 |
+
/--
|
| 192 |
+
Try to synthesize metavariables are `instMVars` using type class resolution.
|
| 193 |
+
The ones that cannot be synthesized yet are registered.
|
| 194 |
+
-/
|
| 195 |
+
def synthesizeAppInstMVars : M Unit := do
|
| 196 |
+
Term.synthesizeAppInstMVars (β get).instMVars (β get).f
|
| 197 |
+
modify ({ Β· with instMVars := #[] })
|
| 198 |
+
|
| 199 |
+
/-- fType may become a forallE after we synthesize pending metavariables. -/
|
| 200 |
+
private def synthesizePendingAndNormalizeFunType : M Unit := do
|
| 201 |
+
trySynthesizeAppInstMVars
|
| 202 |
+
synthesizeSyntheticMVars
|
| 203 |
+
let s β get
|
| 204 |
+
let fType β whnfForall s.fType
|
| 205 |
+
if fType.isForall then
|
| 206 |
+
modify fun s => { s with fType }
|
| 207 |
+
else
|
| 208 |
+
if let some f β coerceToFunction? s.f then
|
| 209 |
+
let fType β inferType f
|
| 210 |
+
modify fun s => { s with f, fType }
|
| 211 |
+
else
|
| 212 |
+
for namedArg in s.namedArgs do
|
| 213 |
+
let f := s.f.getAppFn
|
| 214 |
+
if f.isConst then
|
| 215 |
+
throwInvalidNamedArg namedArg f.constName!
|
| 216 |
+
else
|
| 217 |
+
throwInvalidNamedArg namedArg none
|
| 218 |
+
-- Help users see if this is actually due to an indentation mismatch/other parsing mishaps:
|
| 219 |
+
let extra := if let some (arg : Arg) := s.args[0]? then
|
| 220 |
+
.note m!"Expected a function because this term is being applied to the argument\
|
| 221 |
+
{indentD <| toMessageData arg}"
|
| 222 |
+
else .nil
|
| 223 |
+
throwError "Function expected at{indentExpr s.f}\nbut this term has type{indentExpr fType}{extra}"
|
| 224 |
+
|
| 225 |
+
/-- Normalize and return the function type. -/
|
| 226 |
+
private def normalizeFunType : M Expr := do
|
| 227 |
+
let s β get
|
| 228 |
+
let fType β whnfForall s.fType
|
| 229 |
+
modify fun s => { s with fType }
|
| 230 |
+
return fType
|
| 231 |
+
|
| 232 |
+
/-- Return the binder name at `fType`. This method assumes `fType` is a function type. -/
|
| 233 |
+
private def getBindingName : M Name := return (β get).fType.bindingName!
|
| 234 |
+
|
| 235 |
+
/-- Return the next argument expected type. This method assumes `fType` is a function type. -/
|
| 236 |
+
private def getArgExpectedType : M Expr := return (β get).fType.bindingDomain!
|
| 237 |
+
|
| 238 |
+
/-- Remove named argument with name `binderName` from `namedArgs`. -/
|
| 239 |
+
def eraseNamedArg (binderName : Name) : M Unit :=
|
| 240 |
+
modify fun s => { s with namedArgs := Term.eraseNamedArg s.namedArgs binderName }
|
| 241 |
+
|
| 242 |
+
/--
|
| 243 |
+
Add a new argument to the result. That is, `f := f arg`, update `fType`.
|
| 244 |
+
This method assumes `fType` is a function type. -/
|
| 245 |
+
private def addNewArg (argName : Name) (arg : Expr) : M Unit := do
|
| 246 |
+
modify fun s => { s with f := mkApp s.f arg, fType := s.fType.bindingBody!.instantiate1 arg }
|
| 247 |
+
if arg.isMVar then
|
| 248 |
+
registerMVarArgName arg.mvarId! argName
|
| 249 |
+
|
| 250 |
+
/--
|
| 251 |
+
Elaborate the given `Arg` and add it to the result. See `addNewArg`.
|
| 252 |
+
Recall that, `Arg` may be wrapping an already elaborated `Expr`. -/
|
| 253 |
+
private def elabAndAddNewArg (argName : Name) (arg : Arg) : M Unit := do
|
| 254 |
+
let s β get
|
| 255 |
+
let expectedType := (β getArgExpectedType).consumeTypeAnnotations
|
| 256 |
+
match arg with
|
| 257 |
+
| Arg.expr val =>
|
| 258 |
+
let arg β ensureArgType s.f val expectedType
|
| 259 |
+
addNewArg argName arg
|
| 260 |
+
| Arg.stx stx =>
|
| 261 |
+
let val β elabTerm stx expectedType
|
| 262 |
+
let arg β withRef stx <| ensureArgType s.f val expectedType
|
| 263 |
+
addNewArg argName arg
|
| 264 |
+
|
| 265 |
+
/-- Return true if `fType` contains `OptParam` or `AutoParams` -/
|
| 266 |
+
private def fTypeHasOptAutoParams : M Bool := do
|
| 267 |
+
hasOptAutoParams (β get).fType
|
| 268 |
+
|
| 269 |
+
/--
|
| 270 |
+
Auxiliary function for retrieving the resulting type of a function application.
|
| 271 |
+
See `propagateExpectedType`.
|
| 272 |
+
Remark: `(explicit : Bool) == true` when `@` modifier is used. -/
|
| 273 |
+
private partial def getForallBody (explicit : Bool) : Nat β List NamedArg β Expr β Option Expr
|
| 274 |
+
| i, namedArgs, type@(.forallE n d b bi) =>
|
| 275 |
+
match findBinderName? namedArgs n with
|
| 276 |
+
| some _ => getForallBody explicit i (Term.eraseNamedArg namedArgs n) b
|
| 277 |
+
| none =>
|
| 278 |
+
if !explicit && !bi.isExplicit then
|
| 279 |
+
getForallBody explicit i namedArgs b
|
| 280 |
+
else if i > 0 then
|
| 281 |
+
getForallBody explicit (i-1) namedArgs b
|
| 282 |
+
else if d.isAutoParam || d.isOptParam then
|
| 283 |
+
getForallBody explicit i namedArgs b
|
| 284 |
+
else
|
| 285 |
+
some type
|
| 286 |
+
| 0, [], type => some type
|
| 287 |
+
| _, _, _ => none
|
| 288 |
+
|
| 289 |
+
private def shouldPropagateExpectedTypeFor (nextArg : Arg) : Bool :=
|
| 290 |
+
match nextArg with
|
| 291 |
+
| .expr _ => false -- it has already been elaborated
|
| 292 |
+
| .stx stx =>
|
| 293 |
+
-- TODO: make this configurable?
|
| 294 |
+
stx.getKind != ``Lean.Parser.Term.hole &&
|
| 295 |
+
stx.getKind != ``Lean.Parser.Term.syntheticHole &&
|
| 296 |
+
stx.getKind != ``Lean.Parser.Term.byTactic
|
| 297 |
+
|
| 298 |
+
/--
|
| 299 |
+
Auxiliary method for propagating the expected type. We call it as soon as we find the first explicit
|
| 300 |
+
argument. The goal is to propagate the expected type in applications of functions such as
|
| 301 |
+
```lean
|
| 302 |
+
Add.add {Ξ± : Type u} : Ξ± β Ξ± β Ξ±
|
| 303 |
+
List.cons {Ξ± : Type u} : Ξ± β List Ξ± β List Ξ±
|
| 304 |
+
```
|
| 305 |
+
This is particularly useful when there applicable coercions. For example,
|
| 306 |
+
assume we have a coercion from `Nat` to `Int`, and we have
|
| 307 |
+
`(x : Nat)` and the expected type is `List Int`. Then, if we don't use this function,
|
| 308 |
+
the elaborator will fail to elaborate
|
| 309 |
+
```
|
| 310 |
+
List.cons x []
|
| 311 |
+
```
|
| 312 |
+
First, the elaborator creates a new metavariable `?Ξ±` for the implicit argument `{Ξ± : Type u}`.
|
| 313 |
+
Then, when it processes `x`, it assigns `?Ξ± := Nat`, and then obtains the
|
| 314 |
+
resultant type `List Nat` which is **not** definitionally equal to `List Int`.
|
| 315 |
+
We solve the problem by executing this method before we elaborate the first explicit argument (`x` in this example).
|
| 316 |
+
This method infers that the resultant type is `List ?Ξ±` and unifies it with `List Int`.
|
| 317 |
+
Then, when we elaborate `x`, the elaborate realizes the coercion from `Nat` to `Int` must be used, and the
|
| 318 |
+
term
|
| 319 |
+
```
|
| 320 |
+
@List.cons Int (coe x) (@List.nil Int)
|
| 321 |
+
```
|
| 322 |
+
is produced.
|
| 323 |
+
|
| 324 |
+
The method will do nothing if
|
| 325 |
+
1- The resultant type depends on the remaining arguments (i.e., `!eTypeBody.hasLooseBVars`).
|
| 326 |
+
2- The resultant type contains optional/auto params.
|
| 327 |
+
|
| 328 |
+
We have considered adding the following extra conditions
|
| 329 |
+
a) The resultant type does not contain any type metavariable.
|
| 330 |
+
b) The resultant type contains a nontype metavariable.
|
| 331 |
+
|
| 332 |
+
These two conditions would restrict the method to simple functions that are "morally" in
|
| 333 |
+
the Hindley&Milner fragment.
|
| 334 |
+
If users need to disable expected type propagation, we can add an attribute `[elab_without_expected_type]`.
|
| 335 |
+
-/
|
| 336 |
+
private def propagateExpectedType (arg : Arg) : M Unit := do
|
| 337 |
+
if shouldPropagateExpectedTypeFor arg then
|
| 338 |
+
let s β get
|
| 339 |
+
-- TODO: handle s.etaArgs.size > 0
|
| 340 |
+
unless !s.etaArgs.isEmpty || !s.propagateExpected do
|
| 341 |
+
match s.expectedType? with
|
| 342 |
+
| none => pure ()
|
| 343 |
+
| some expectedType =>
|
| 344 |
+
/- We don't propagate `Prop` because we often use `Prop` as a more general "Bool" (e.g., `if-then-else`).
|
| 345 |
+
If we propagate `expectedType == Prop` in the following examples, the elaborator would fail
|
| 346 |
+
```
|
| 347 |
+
def f1 (s : Nat Γ Bool) : Bool := if s.2 then false else true
|
| 348 |
+
|
| 349 |
+
def f2 (s : List Bool) : Bool := if s.head! then false else true
|
| 350 |
+
|
| 351 |
+
def f3 (s : List Bool) : Bool := if List.head! (s.map not) then false else true
|
| 352 |
+
```
|
| 353 |
+
They would all fail for the same reason. So, let's focus on the first one.
|
| 354 |
+
We would elaborate `s.2` with `expectedType == Prop`.
|
| 355 |
+
Before we elaborate `s`, this method would be invoked, and `s.fType` is `?Ξ± Γ ?Ξ² β ?Ξ²` and after
|
| 356 |
+
propagation we would have `?Ξ± Γ Prop β Prop`. Then, when we would try to elaborate `s`, and
|
| 357 |
+
get a type error because `?Ξ± Γ Prop` cannot be unified with `Nat Γ Bool`.
|
| 358 |
+
Most users would have a hard time trying to understand why these examples failed.
|
| 359 |
+
|
| 360 |
+
Here is a possible alternative workaround. We give up the idea of using `Prop` at `if-then-else`.
|
| 361 |
+
Drawback: users use `if-then-else` with conditions that are not Decidable.
|
| 362 |
+
So, users would have to embrace `propDecidable` and `choice`.
|
| 363 |
+
This may not be that bad since the developers and users don't seem to care about constructivism.
|
| 364 |
+
|
| 365 |
+
We currently use a different workaround, we just don't propagate the expected type when it is `Prop`. -/
|
| 366 |
+
if expectedType.isProp then
|
| 367 |
+
modify fun s => { s with propagateExpected := false }
|
| 368 |
+
else
|
| 369 |
+
let numRemainingArgs := s.args.length
|
| 370 |
+
trace[Elab.app.propagateExpectedType] "etaArgs.size: {s.etaArgs.size}, numRemainingArgs: {numRemainingArgs}, fType: {s.fType}"
|
| 371 |
+
match getForallBody (β read).explicit numRemainingArgs s.namedArgs s.fType with
|
| 372 |
+
| none => pure ()
|
| 373 |
+
| some fTypeBody =>
|
| 374 |
+
unless fTypeBody.hasLooseBVars do
|
| 375 |
+
unless (β hasOptAutoParams fTypeBody) do
|
| 376 |
+
trySynthesizeAppInstMVars
|
| 377 |
+
trace[Elab.app.propagateExpectedType] "{expectedType} =?= {fTypeBody}"
|
| 378 |
+
if (β isDefEq expectedType fTypeBody) then
|
| 379 |
+
/- Note that we only set `propagateExpected := false` when propagation has succeeded. -/
|
| 380 |
+
modify fun s => { s with propagateExpected := false }
|
| 381 |
+
|
| 382 |
+
/-- This method executes after all application arguments have been processed. -/
|
| 383 |
+
private def finalize : M Expr := do
|
| 384 |
+
let s β get
|
| 385 |
+
let mut e := s.f
|
| 386 |
+
-- all user explicit arguments have been consumed
|
| 387 |
+
trace[Elab.app.finalize] e
|
| 388 |
+
let ref β getRef
|
| 389 |
+
-- Register the error context of implicits
|
| 390 |
+
for mvarId in s.toSetErrorCtx do
|
| 391 |
+
registerMVarErrorImplicitArgInfo mvarId ref e
|
| 392 |
+
if !s.etaArgs.isEmpty then
|
| 393 |
+
e β mkLambdaFVars s.etaArgs e
|
| 394 |
+
/-
|
| 395 |
+
Remark: we should not use `s.fType` as `eType` even when
|
| 396 |
+
`s.etaArgs.isEmpty`. Reason: it may have been unfolded.
|
| 397 |
+
-/
|
| 398 |
+
let eType β inferType e
|
| 399 |
+
trace[Elab.app.finalize] "after etaArgs, {e} : {eType}"
|
| 400 |
+
/- Recall that `resultTypeOutParam? = some mvarId` if the function result type is the output parameter
|
| 401 |
+
of a local instance. The value of this parameter may be inferable using other arguments. For example,
|
| 402 |
+
suppose we have
|
| 403 |
+
```lean
|
| 404 |
+
def add_one {X} [Trait X] [One (Trait.R X)] [HAdd X (Trait.R X) X] (x : X) : X := x + (One.one : (Trait.R X))
|
| 405 |
+
```
|
| 406 |
+
from test `948.lean`. There are multiple ways to infer `X`, and we don't want to mark it as `syntheticOpaque`.
|
| 407 |
+
-/
|
| 408 |
+
if let some outParamMVarId := s.resultTypeOutParam? then
|
| 409 |
+
synthesizeAppInstMVars
|
| 410 |
+
/- If `eType != mkMVar outParamMVarId`, then the
|
| 411 |
+
function is partially applied, and we do not apply default instances. -/
|
| 412 |
+
if !(β outParamMVarId.isAssigned) && eType.isMVar && eType.mvarId! == outParamMVarId then
|
| 413 |
+
synthesizeSyntheticMVarsUsingDefault
|
| 414 |
+
return e
|
| 415 |
+
else
|
| 416 |
+
return e
|
| 417 |
+
if let some expectedType := s.expectedType? then
|
| 418 |
+
trySynthesizeAppInstMVars
|
| 419 |
+
-- Try to propagate expected type. Ignore if types are not definitionally equal, caller must handle it.
|
| 420 |
+
trace[Elab.app.finalize] "expected type: {expectedType}"
|
| 421 |
+
discard <| isDefEq expectedType eType
|
| 422 |
+
synthesizeAppInstMVars
|
| 423 |
+
return e
|
| 424 |
+
|
| 425 |
+
/--
|
| 426 |
+
Returns a named argument that depends on the next argument, otherwise `none`.
|
| 427 |
+
-/
|
| 428 |
+
private def findNamedArgDependsOnCurrent? : M (Option NamedArg) := do
|
| 429 |
+
let s β get
|
| 430 |
+
if s.namedArgs.isEmpty then
|
| 431 |
+
return none
|
| 432 |
+
else
|
| 433 |
+
forallTelescopeReducing s.fType fun xs _ => do
|
| 434 |
+
let curr := xs[0]!
|
| 435 |
+
for h : i in [1:xs.size] do
|
| 436 |
+
let xDecl β xs[i].fvarId!.getDecl
|
| 437 |
+
if let some arg := s.namedArgs.find? fun arg => arg.name == xDecl.userName then
|
| 438 |
+
/- Remark: a default value at `optParam` does not count as a dependency -/
|
| 439 |
+
if (β exprDependsOn xDecl.type.cleanupAnnotations curr.fvarId!) then
|
| 440 |
+
return arg
|
| 441 |
+
return none
|
| 442 |
+
|
| 443 |
+
|
| 444 |
+
/-- Return `true` if there are regular or named arguments to be processed. -/
|
| 445 |
+
private def hasArgsToProcess : M Bool := do
|
| 446 |
+
let s β get
|
| 447 |
+
return !s.args.isEmpty || !s.namedArgs.isEmpty
|
| 448 |
+
|
| 449 |
+
/--
|
| 450 |
+
Returns the argument syntax if the next argument at `args` is of the form `_`.
|
| 451 |
+
-/
|
| 452 |
+
private def nextArgHole? : M (Option Syntax) := do
|
| 453 |
+
match (β get).args with
|
| 454 |
+
| Arg.stx stx@(Syntax.node _ ``Lean.Parser.Term.hole _) :: _ => pure stx
|
| 455 |
+
| _ => pure none
|
| 456 |
+
|
| 457 |
+
/--
|
| 458 |
+
Return `true` if the next argument to be processed is the outparam of a local instance, and it the result type
|
| 459 |
+
of the function.
|
| 460 |
+
|
| 461 |
+
For example, suppose we have the class
|
| 462 |
+
```lean
|
| 463 |
+
class Get (Cont : Type u) (Idx : Type v) (Elem : outParam (Type w)) where
|
| 464 |
+
get (xs : Cont) (i : Idx) : Elem
|
| 465 |
+
```
|
| 466 |
+
And the current value of `fType` is
|
| 467 |
+
```
|
| 468 |
+
{Cont : Type u_1} β {Idx : Type u_2} β {Elem : Type u_3} β [self : Get Cont Idx Elem] β Cont β Idx β Elem
|
| 469 |
+
```
|
| 470 |
+
then the result returned by this method is `false` since `Cont` is not the output param of any local instance.
|
| 471 |
+
Now assume `fType` is
|
| 472 |
+
```
|
| 473 |
+
{Elem : Type u_3} β [self : Get Cont Idx Elem] β Cont β Idx β Elem
|
| 474 |
+
```
|
| 475 |
+
then, the method returns `true` because `Elem` is an output parameter for the local instance `[self : Get Cont Idx Elem]`.
|
| 476 |
+
|
| 477 |
+
Remark: if `resultIsOutParamSupport` is `false`, this method returns `false`.
|
| 478 |
+
-/
|
| 479 |
+
private partial def isNextOutParamOfLocalInstanceAndResult : M Bool := do
|
| 480 |
+
if !(β read).resultIsOutParamSupport then
|
| 481 |
+
return false
|
| 482 |
+
let type := (β get).fType.bindingBody!
|
| 483 |
+
unless isResultType type 0 do
|
| 484 |
+
return false
|
| 485 |
+
if (β hasLocalInstaceWithOutParams type) then
|
| 486 |
+
let x := mkFVar (β mkFreshFVarId)
|
| 487 |
+
isOutParamOfLocalInstance x (type.instantiate1 x)
|
| 488 |
+
else
|
| 489 |
+
return false
|
| 490 |
+
where
|
| 491 |
+
isResultType (type : Expr) (i : Nat) : Bool :=
|
| 492 |
+
match type with
|
| 493 |
+
| .forallE _ _ b _ => isResultType b (i + 1)
|
| 494 |
+
| .bvar idx => idx == i
|
| 495 |
+
| _ => false
|
| 496 |
+
|
| 497 |
+
/-- (quick filter) Return true if `type` contains a binder `[C ...]` where `C` is a class containing outparams. -/
|
| 498 |
+
hasLocalInstaceWithOutParams (type : Expr) : CoreM Bool := do
|
| 499 |
+
let .forallE _ d b bi := type | return false
|
| 500 |
+
if bi.isInstImplicit then
|
| 501 |
+
if let .const declName .. := d.getAppFn then
|
| 502 |
+
if hasOutParams (β getEnv) declName then
|
| 503 |
+
return true
|
| 504 |
+
hasLocalInstaceWithOutParams b
|
| 505 |
+
|
| 506 |
+
isOutParamOfLocalInstance (x : Expr) (type : Expr) : MetaM Bool := do
|
| 507 |
+
let .forallE _ d b bi := type | return false
|
| 508 |
+
if bi.isInstImplicit then
|
| 509 |
+
if let .const declName .. := d.getAppFn then
|
| 510 |
+
if hasOutParams (β getEnv) declName then
|
| 511 |
+
let cType β inferType d.getAppFn
|
| 512 |
+
if (β isOutParamOf x 0 d.getAppArgs cType) then
|
| 513 |
+
return true
|
| 514 |
+
isOutParamOfLocalInstance x b
|
| 515 |
+
|
| 516 |
+
isOutParamOf (x : Expr) (i : Nat) (args : Array Expr) (cType : Expr) : MetaM Bool := do
|
| 517 |
+
if h : i < args.size then
|
| 518 |
+
match (β whnf cType) with
|
| 519 |
+
| .forallE _ d b _ =>
|
| 520 |
+
if args[i] == x && d.isOutParam then
|
| 521 |
+
return true
|
| 522 |
+
isOutParamOf x (i+1) args b
|
| 523 |
+
| _ => return false
|
| 524 |
+
else
|
| 525 |
+
return false
|
| 526 |
+
|
| 527 |
+
mutual
|
| 528 |
+
/--
|
| 529 |
+
Create a fresh local variable with the current binder name and argument type, add it to `etaArgs` and `f`,
|
| 530 |
+
and then execute the main loop.
|
| 531 |
+
-/
|
| 532 |
+
private partial def addEtaArg (argName : Name) : M Expr := do
|
| 533 |
+
let n β getBindingName
|
| 534 |
+
let type β getArgExpectedType
|
| 535 |
+
withLocalDeclD n type fun x => do
|
| 536 |
+
modify fun s => { s with etaArgs := s.etaArgs.push x }
|
| 537 |
+
addNewArg argName x
|
| 538 |
+
main
|
| 539 |
+
|
| 540 |
+
/--
|
| 541 |
+
Create a fresh metavariable for the implicit argument, add it to `f`, and then execute the main loop.
|
| 542 |
+
-/
|
| 543 |
+
private partial def addImplicitArg (argName : Name) : M Expr := do
|
| 544 |
+
let argType β getArgExpectedType
|
| 545 |
+
let arg β if (β isNextOutParamOfLocalInstanceAndResult) then
|
| 546 |
+
let arg β mkFreshExprMVar argType
|
| 547 |
+
/- When the result type is an output parameter, we don't want to propagate the expected type.
|
| 548 |
+
So, we just mark `propagateExpected := false` to disable it.
|
| 549 |
+
At `finalize`, we check whether `arg` is still unassigned, if it is, we apply default instances,
|
| 550 |
+
and try to synthesize pending mvars. -/
|
| 551 |
+
modify fun s => { s with resultTypeOutParam? := some arg.mvarId!, propagateExpected := false }
|
| 552 |
+
pure arg
|
| 553 |
+
else
|
| 554 |
+
mkFreshExprMVar argType
|
| 555 |
+
modify fun s => { s with toSetErrorCtx := s.toSetErrorCtx.push arg.mvarId! }
|
| 556 |
+
addNewArg argName arg
|
| 557 |
+
main
|
| 558 |
+
|
| 559 |
+
/--
|
| 560 |
+
Process a `fType` of the form `(x : A) β B x`.
|
| 561 |
+
This method assume `fType` is a function type.
|
| 562 |
+
-/
|
| 563 |
+
private partial def processExplicitArg (argName : Name) : M Expr := do
|
| 564 |
+
match (β get).args with
|
| 565 |
+
| arg::args =>
|
| 566 |
+
-- Note: currently the following test never succeeds in explicit mode since `@x.f` notation does not exist.
|
| 567 |
+
if let some true := NamedArg.suppressDeps <$> (β findNamedArgDependsOnCurrent?) then
|
| 568 |
+
/-
|
| 569 |
+
We treat the explicit argument `argName` as implicit
|
| 570 |
+
if we have a named arguments that depends on it whose `suppressDeps` flag set to `true`.
|
| 571 |
+
The motivation for this is class projections (issue #1851).
|
| 572 |
+
In some cases, class projections can have explicit parameters. For example, in
|
| 573 |
+
```
|
| 574 |
+
class Approx {Ξ± : Type} (a : Ξ±) (X : Type) : Type where
|
| 575 |
+
val : X
|
| 576 |
+
```
|
| 577 |
+
the type of `Approx.val` is `{Ξ± : Type} β (a : Ξ±) β {X : Type} β [self : Approx a X] β X`.
|
| 578 |
+
Note that the parameter `a` is explicit since there is no way to infer it from the expected
|
| 579 |
+
type or from the types of other explicit parameters.
|
| 580 |
+
Being a parameter of the class, `a` is determined by the type of `self`.
|
| 581 |
+
|
| 582 |
+
Consider
|
| 583 |
+
```
|
| 584 |
+
variable {Ξ± Ξ² X Y : Type} {f' : Ξ± β Ξ²} {x' : Ξ±} [f : Approx f' (X β Y)]
|
| 585 |
+
```
|
| 586 |
+
Recall that `f.val` is, to first approximation, sugar for `Approx.val (self := f)`.
|
| 587 |
+
Without further refinement, this would expand to `fun f'' : Ξ± β Ξ² => Approx.val f'' f`,
|
| 588 |
+
which is a type error, since `f''` must be defeq to `f'`.
|
| 589 |
+
Furthermore, with projection notation, users expect all structure parameters
|
| 590 |
+
to be uniformly implicit; after all, they are determined by `self`.
|
| 591 |
+
To handle this, the `(self := f)` named argument is annotated with the `suppressDeps` flag.
|
| 592 |
+
This causes the `a` parameter to become implicit, and `f.val` instead expands to `Approx.val f' f`.
|
| 593 |
+
|
| 594 |
+
This feature previously was enabled for *all* explicit arguments, which confused users
|
| 595 |
+
and was frequently reported as a bug (issue #1867).
|
| 596 |
+
Now it is only enabled for the `self` argument in structure projections.
|
| 597 |
+
|
| 598 |
+
We used to do this only when `(β get).args` was empty,
|
| 599 |
+
but it created an asymmetry because `f.val` worked as expected,
|
| 600 |
+
yet one would have to write `f.val _ x` when there are further arguments.
|
| 601 |
+
-/
|
| 602 |
+
return (β addImplicitArg argName)
|
| 603 |
+
propagateExpectedType arg
|
| 604 |
+
modify fun s => { s with args }
|
| 605 |
+
elabAndAddNewArg argName arg
|
| 606 |
+
main
|
| 607 |
+
| _ =>
|
| 608 |
+
if (β read).ellipsis && (β readThe Term.Context).inPattern then
|
| 609 |
+
/-
|
| 610 |
+
In patterns, ellipsis should always be an implicit argument, even if it is an optparam or autoparam.
|
| 611 |
+
This prevents examples such as the one in #4555 from failing:
|
| 612 |
+
```lean
|
| 613 |
+
match e with
|
| 614 |
+
| .internal .. => sorry
|
| 615 |
+
| .error .. => sorry
|
| 616 |
+
```
|
| 617 |
+
The `internal` has an optparam (`| internal (id : InternalExceptionId) (extra : KVMap := {})`).
|
| 618 |
+
|
| 619 |
+
We may consider having ellipsis suppress optparams and autoparams in general.
|
| 620 |
+
We avoid doing so for now since it's possible to opt-out of them (for example with `.internal (extra := _) ..`)
|
| 621 |
+
but it's not possible to opt-in.
|
| 622 |
+
-/
|
| 623 |
+
return β addImplicitArg argName
|
| 624 |
+
let argType β getArgExpectedType
|
| 625 |
+
match (β read).explicit, argType.getOptParamDefault?, argType.getAutoParamTactic? with
|
| 626 |
+
| false, some defVal, _ => addNewArg argName defVal; main
|
| 627 |
+
| false, _, some (.const tacticDecl _) =>
|
| 628 |
+
let env β getEnv
|
| 629 |
+
let opts β getOptions
|
| 630 |
+
match evalSyntaxConstant env opts tacticDecl with
|
| 631 |
+
| Except.error err => throwError err
|
| 632 |
+
| Except.ok tacticSyntax =>
|
| 633 |
+
let tacticBlock β `(by $(β¨tacticSyntaxβ©))
|
| 634 |
+
/-
|
| 635 |
+
We insert position information from the current ref into `stx` everywhere, simulating this being
|
| 636 |
+
a tactic script inserted by the user, which ensures error messages and logging will always be attributed
|
| 637 |
+
to this application rather than sometimes being placed at position (1,0) in the file.
|
| 638 |
+
Placing position information on `by` syntax alone is not sufficient since incrementality
|
| 639 |
+
(in particular, `Lean.Elab.Term.withReuseContext`) controls the ref to avoid leakage of outside data.
|
| 640 |
+
Note that `tacticSyntax` contains no position information itself, since it is erased by `Lean.Elab.Term.quoteAutoTactic`.
|
| 641 |
+
-/
|
| 642 |
+
let info := (β getRef).getHeadInfo.nonCanonicalSynthetic
|
| 643 |
+
let tacticBlock := tacticBlock.raw.rewriteBottomUp (Β·.setInfo info)
|
| 644 |
+
let mvar β mkTacticMVar argType.consumeTypeAnnotations tacticBlock (.autoParam argName)
|
| 645 |
+
-- Note(kmill): We are adding terminfo to simulate a previous implementation that elaborated `tacticBlock`.
|
| 646 |
+
-- We should look into removing this since terminfo for synthetic syntax is suspect,
|
| 647 |
+
-- but we noted it was necessary to preserve the behavior of the unused variable linter.
|
| 648 |
+
addTermInfo' tacticBlock mvar
|
| 649 |
+
let argNew := Arg.expr mvar
|
| 650 |
+
propagateExpectedType argNew
|
| 651 |
+
elabAndAddNewArg argName argNew
|
| 652 |
+
main
|
| 653 |
+
| false, _, some _ =>
|
| 654 |
+
throwError "invalid autoParam, argument must be a constant"
|
| 655 |
+
| _, _, _ =>
|
| 656 |
+
if (β read).ellipsis then
|
| 657 |
+
addImplicitArg argName
|
| 658 |
+
else if !(β get).namedArgs.isEmpty then
|
| 659 |
+
if let some _ β findNamedArgDependsOnCurrent? then
|
| 660 |
+
/-
|
| 661 |
+
Dependencies of named arguments cannot be turned into eta arguments
|
| 662 |
+
since they are determined by the named arguments.
|
| 663 |
+
Instead we can turn them into implicit arguments.
|
| 664 |
+
-/
|
| 665 |
+
addImplicitArg argName
|
| 666 |
+
else
|
| 667 |
+
addEtaArg argName
|
| 668 |
+
else if !(β read).explicit then
|
| 669 |
+
if (β fTypeHasOptAutoParams) then
|
| 670 |
+
addEtaArg argName
|
| 671 |
+
else
|
| 672 |
+
finalize
|
| 673 |
+
else
|
| 674 |
+
finalize
|
| 675 |
+
|
| 676 |
+
/--
|
| 677 |
+
Process a `fType` of the form `{x : A} β B x`.
|
| 678 |
+
This method assume `fType` is a function type -/
|
| 679 |
+
private partial def processImplicitArg (argName : Name) : M Expr := do
|
| 680 |
+
if (β read).explicit then
|
| 681 |
+
processExplicitArg argName
|
| 682 |
+
else
|
| 683 |
+
addImplicitArg argName
|
| 684 |
+
|
| 685 |
+
/--
|
| 686 |
+
Process a `fType` of the form `{{x : A}} β B x`.
|
| 687 |
+
This method assume `fType` is a function type -/
|
| 688 |
+
private partial def processStrictImplicitArg (argName : Name) : M Expr := do
|
| 689 |
+
if (β read).explicit then
|
| 690 |
+
processExplicitArg argName
|
| 691 |
+
else if (β hasArgsToProcess) then
|
| 692 |
+
addImplicitArg argName
|
| 693 |
+
else
|
| 694 |
+
finalize
|
| 695 |
+
|
| 696 |
+
/--
|
| 697 |
+
Process a `fType` of the form `[x : A] β B x`.
|
| 698 |
+
This method assume `fType` is a function type.
|
| 699 |
+
-/
|
| 700 |
+
private partial def processInstImplicitArg (argName : Name) : M Expr := do
|
| 701 |
+
if (β read).explicit then
|
| 702 |
+
if let some stx β nextArgHole? then
|
| 703 |
+
-- We still use typeclass resolution for `_` arguments.
|
| 704 |
+
-- This behavior can be suppressed with `(_)`.
|
| 705 |
+
let ty β getArgExpectedType
|
| 706 |
+
let arg β mkInstMVar ty
|
| 707 |
+
addTermInfo' stx arg ty
|
| 708 |
+
modify fun s => { s with args := s.args.tail! }
|
| 709 |
+
main
|
| 710 |
+
else
|
| 711 |
+
processExplicitArg argName
|
| 712 |
+
else
|
| 713 |
+
discard <| mkInstMVar (β getArgExpectedType)
|
| 714 |
+
main
|
| 715 |
+
where
|
| 716 |
+
mkInstMVar (ty : Expr) : M Expr := do
|
| 717 |
+
let arg β mkFreshExprMVar ty MetavarKind.synthetic
|
| 718 |
+
addInstMVar arg.mvarId!
|
| 719 |
+
addNewArg argName arg
|
| 720 |
+
return arg
|
| 721 |
+
|
| 722 |
+
/-- Elaborate function application arguments. -/
|
| 723 |
+
partial def main : M Expr := do
|
| 724 |
+
let fType β normalizeFunType
|
| 725 |
+
if fType.isForall then
|
| 726 |
+
let binderName := fType.bindingName!
|
| 727 |
+
let binfo := fType.bindingInfo!
|
| 728 |
+
let s β get
|
| 729 |
+
match findBinderName? s.namedArgs binderName with
|
| 730 |
+
| some namedArg =>
|
| 731 |
+
propagateExpectedType namedArg.val
|
| 732 |
+
eraseNamedArg binderName
|
| 733 |
+
elabAndAddNewArg binderName namedArg.val
|
| 734 |
+
main
|
| 735 |
+
| none =>
|
| 736 |
+
match binfo with
|
| 737 |
+
| .implicit => processImplicitArg binderName
|
| 738 |
+
| .instImplicit => processInstImplicitArg binderName
|
| 739 |
+
| .strictImplicit => processStrictImplicitArg binderName
|
| 740 |
+
| _ => processExplicitArg binderName
|
| 741 |
+
else if (β hasArgsToProcess) then
|
| 742 |
+
synthesizePendingAndNormalizeFunType
|
| 743 |
+
main
|
| 744 |
+
else
|
| 745 |
+
finalize
|
| 746 |
+
|
| 747 |
+
end
|
| 748 |
+
|
| 749 |
+
end ElabAppArgs
|
| 750 |
+
|
| 751 |
+
|
| 752 |
+
/-! # Eliminator-like function application elaborator -/
|
| 753 |
+
|
| 754 |
+
/--
|
| 755 |
+
Information about an eliminator used by the elab-as-elim elaborator.
|
| 756 |
+
This is not to be confused with `Lean.Meta.ElimInfo`, which is for `induction` and `cases`.
|
| 757 |
+
The elab-as-elim routine is less restrictive in what counts as an eliminator, and it doesn't need
|
| 758 |
+
to have a strict notion of what is a "target" β all it cares about are
|
| 759 |
+
1. that the return type of a function is of the form `m ...` where `m` is a parameter
|
| 760 |
+
(unlike `induction` and `cases` eliminators, the arguments to `m`, known as "discriminants",
|
| 761 |
+
can be any expressions, not just parameters), and
|
| 762 |
+
2. which arguments should be eagerly elaborated, to make discriminants be as elaborated as
|
| 763 |
+
possible for the expected type generalization procedure,
|
| 764 |
+
and which should be postponed (since they are the "minor premises").
|
| 765 |
+
|
| 766 |
+
Note that the routine isn't doing induction/cases *on* particular expressions.
|
| 767 |
+
The purpose of elab-as-elim is to successfully solve the higher-order unification problem
|
| 768 |
+
between the return type of the function and the expected type.
|
| 769 |
+
-/
|
| 770 |
+
structure ElabElimInfo where
|
| 771 |
+
/-- The eliminator. -/
|
| 772 |
+
elimExpr : Expr
|
| 773 |
+
/-- The type of the eliminator. -/
|
| 774 |
+
elimType : Expr
|
| 775 |
+
/-- The position of the motive parameter. -/
|
| 776 |
+
motivePos : Nat
|
| 777 |
+
/--
|
| 778 |
+
Positions of "major" parameters (those that should be eagerly elaborated
|
| 779 |
+
because they can contribute to the motive inference procedure).
|
| 780 |
+
All parameters that are neither the motive nor a major parameter are "minor" parameters.
|
| 781 |
+
The major parameters include all of the parameters that transitively appear in the motive's arguments,
|
| 782 |
+
as well as "first-order" arguments that include such parameters,
|
| 783 |
+
since they too can help with elaborating discriminants.
|
| 784 |
+
|
| 785 |
+
For example, in the following theorem the argument `h : a = b`
|
| 786 |
+
should be elaborated eagerly because it contains `b`, which occurs in `motive b`.
|
| 787 |
+
```
|
| 788 |
+
theorem Eq.subst' {Ξ±} {motive : Ξ± β Prop} {a b : Ξ±} (h : a = b) : motive a β motive b
|
| 789 |
+
```
|
| 790 |
+
For another example, the term `isEmptyElim (Ξ± := Ξ±)` is an underapplied eliminator, and it needs
|
| 791 |
+
argument `Ξ±` to be elaborated eagerly to create a type-correct motive.
|
| 792 |
+
```
|
| 793 |
+
def isEmptyElim [IsEmpty Ξ±] {p : Ξ± β Sort _} (a : Ξ±) : p a := ...
|
| 794 |
+
example {Ξ± : Type _} [IsEmpty Ξ±] : id (Ξ± β False) := isEmptyElim (Ξ± := Ξ±)
|
| 795 |
+
```
|
| 796 |
+
-/
|
| 797 |
+
majorsPos : Array Nat := #[]
|
| 798 |
+
deriving Repr, Inhabited
|
| 799 |
+
|
| 800 |
+
def getElabElimExprInfo (elimExpr : Expr) : MetaM ElabElimInfo := do
|
| 801 |
+
let elimType β inferType elimExpr
|
| 802 |
+
trace[Elab.app.elab_as_elim] "eliminator {indentExpr elimExpr}\nhas type{indentExpr elimType}"
|
| 803 |
+
forallTelescopeReducing elimType fun xs type => do
|
| 804 |
+
let motive := type.getAppFn
|
| 805 |
+
let motiveArgs := type.getAppArgs
|
| 806 |
+
unless motive.isFVar && motiveArgs.size > 0 do
|
| 807 |
+
throwError "unexpected eliminator resulting type{indentExpr type}"
|
| 808 |
+
let motiveType β inferType motive
|
| 809 |
+
forallTelescopeReducing motiveType fun motiveParams motiveResultType => do
|
| 810 |
+
unless motiveParams.size == motiveArgs.size do
|
| 811 |
+
throwError "unexpected number of arguments at motive type{indentExpr motiveType}"
|
| 812 |
+
unless motiveResultType.isSort do
|
| 813 |
+
throwError "motive result type must be a sort{indentExpr motiveType}"
|
| 814 |
+
let some motivePos := xs.idxOf? motive |
|
| 815 |
+
throwError "unexpected eliminator type{indentExpr elimType}"
|
| 816 |
+
/-
|
| 817 |
+
Compute transitive closure of fvars appearing in arguments to the motive.
|
| 818 |
+
These are the primary set of major parameters.
|
| 819 |
+
-/
|
| 820 |
+
let initMotiveFVars : CollectFVars.State := motiveArgs.foldl (init := {}) collectFVars
|
| 821 |
+
let motiveFVars β xs.size.foldRevM (init := initMotiveFVars) fun i _ s => do
|
| 822 |
+
let x := xs[i]
|
| 823 |
+
if s.fvarSet.contains x.fvarId! then
|
| 824 |
+
return collectFVars s (β inferType x)
|
| 825 |
+
else
|
| 826 |
+
return s
|
| 827 |
+
/- Collect the major parameter positions -/
|
| 828 |
+
let mut majorsPos := #[]
|
| 829 |
+
for h : i in [:xs.size] do
|
| 830 |
+
let x := xs[i]
|
| 831 |
+
unless motivePos == i do
|
| 832 |
+
let xType β x.fvarId!.getType
|
| 833 |
+
/-
|
| 834 |
+
We also consider "first-order" types because we can reliably "extract" information from them.
|
| 835 |
+
We say a term is "first-order" if all applications are of the form `f ...` where `f` is a constant.
|
| 836 |
+
-/
|
| 837 |
+
let isFirstOrder (e : Expr) : Bool := Option.isNone <| e.find? fun e => e.isApp && !e.getAppFn.isConst
|
| 838 |
+
if motiveFVars.fvarSet.contains x.fvarId!
|
| 839 |
+
|| (isFirstOrder xType
|
| 840 |
+
&& Option.isSome (xType.find? fun e => e.isFVar && motiveFVars.fvarSet.contains e.fvarId!)) then
|
| 841 |
+
majorsPos := majorsPos.push i
|
| 842 |
+
trace[Elab.app.elab_as_elim] "motivePos: {motivePos}"
|
| 843 |
+
trace[Elab.app.elab_as_elim] "majorsPos: {majorsPos}"
|
| 844 |
+
return { elimExpr, elimType, motivePos, majorsPos }
|
| 845 |
+
|
| 846 |
+
def getElabElimInfo (elimName : Name) : MetaM ElabElimInfo := do
|
| 847 |
+
getElabElimExprInfo (β mkConstWithFreshMVarLevels elimName)
|
| 848 |
+
|
| 849 |
+
|
| 850 |
+
/--
|
| 851 |
+
Instructs the elaborator that applications of this function should be elaborated like an eliminator.
|
| 852 |
+
|
| 853 |
+
An eliminator is a function that returns an application of a "motive" which is a parameter of the
|
| 854 |
+
form `_ β ... β Sort _`, i.e. a function that takes in a certain amount of arguments (referred to
|
| 855 |
+
as major premises) and returns a type in some universe. The `rec` and `casesOn` functions of
|
| 856 |
+
inductive types are automatically treated as eliminators, for other functions this attribute needs
|
| 857 |
+
to be used.
|
| 858 |
+
|
| 859 |
+
Eliminator elaboration can be compared to the `induction` tactic: The expected type is used as the
|
| 860 |
+
return value of the motive, with occurrences of the major premises replaced with the arguments.
|
| 861 |
+
When more arguments are specified than necessary, the remaining arguments are reverted into the
|
| 862 |
+
expected type.
|
| 863 |
+
|
| 864 |
+
Examples:
|
| 865 |
+
```lean example
|
| 866 |
+
@[elab_as_elim]
|
| 867 |
+
def evenOddRecOn {motive : Nat β Sort u}
|
| 868 |
+
(even : β n, motive (n * 2)) (odd : β n, motive (n * 2 + 1))
|
| 869 |
+
(n : Nat) : motive n := ...
|
| 870 |
+
|
| 871 |
+
-- simple usage
|
| 872 |
+
example (a : Nat) : (a * a) % 2 = a % 2 :=
|
| 873 |
+
evenOddRec _ _ a
|
| 874 |
+
/-
|
| 875 |
+
1. basic motive is `fun n => (a + 2) % 2 = a % 2`
|
| 876 |
+
2. major premise `a` substituted: `fun n => (n + 2) % 2 = n % 2`
|
| 877 |
+
3. now elaborate the other parameters as usual:
|
| 878 |
+
"even" (first hole): expected type `β n, ((n * 2) * (n * 2)) % 2 = (n * 2) % 2`,
|
| 879 |
+
"odd" (second hole): expected type `β n, ((n * 2 + 1) * (n * 2 + 1)) % 2 = (n * 2 + 1) % 2`
|
| 880 |
+
-/
|
| 881 |
+
|
| 882 |
+
-- complex substitution
|
| 883 |
+
example (a : Nat) (f : Nat β Nat) : (f a + 1) % 2 β f a :=
|
| 884 |
+
evenOddRec _ _ (f a)
|
| 885 |
+
/-
|
| 886 |
+
Similar to before, except `f a` is substituted: `motive := fun n => (n + 1) % 2 β n`.
|
| 887 |
+
Now the first hole has expected type `β n, (n * 2 + 1) % 2 β n * 2`.
|
| 888 |
+
Now the second hole has expected type `β n, (n * 2 + 1 + 1) % 2 β n * 2 + 1`.
|
| 889 |
+
-/
|
| 890 |
+
|
| 891 |
+
-- more parameters
|
| 892 |
+
example (a : Nat) (h : a % 2 = 1) : (a + 1) % 2 = 0 :=
|
| 893 |
+
evenOddRec _ _ a h
|
| 894 |
+
/-
|
| 895 |
+
Before substitution, `a % 2 = 1` is reverted: `motive := fun n => a % 2 = 0 β (a + 1) % 2 = 0`.
|
| 896 |
+
Substitution: `motive := fun n => n % 2 = 1 β (n + 1) % 2 = 0`
|
| 897 |
+
Now the first hole has expected type `β n, n * 2 % 2 = 1 β (n * 2) % 2 = 0`.
|
| 898 |
+
Now the second hole has expected type `β n, (n * 2 + 1) % 2 = 1 β (n * 2 + 1) % 2 = 0`.
|
| 899 |
+
-/
|
| 900 |
+
```
|
| 901 |
+
|
| 902 |
+
See also `@[induction_eliminator]` and `@[cases_eliminator]` for registering default eliminators.
|
| 903 |
+
-/
|
| 904 |
+
@[builtin_doc]
|
| 905 |
+
builtin_initialize elabAsElim : TagAttribute β
|
| 906 |
+
registerTagAttribute `elab_as_elim
|
| 907 |
+
"instructs elaborator that the arguments of the function application should be elaborated as were an eliminator"
|
| 908 |
+
/-
|
| 909 |
+
We apply `elab_as_elim` after compilation because this kind of attribute is not applied to auxiliary declarations
|
| 910 |
+
created by the `WF` and `Structural` modules. This is an "indirect" fix for issue #1900. We should consider
|
| 911 |
+
having an explicit flag in attributes to indicate whether they should be copied to auxiliary declarations or not.
|
| 912 |
+
-/
|
| 913 |
+
(applicationTime := .afterCompilation)
|
| 914 |
+
fun declName => do
|
| 915 |
+
let go : MetaM Unit := do
|
| 916 |
+
let info β getConstInfo declName
|
| 917 |
+
if (β hasOptAutoParams info.type) then
|
| 918 |
+
throwError "[elab_as_elim] attribute cannot be used in declarations containing optional and auto parameters"
|
| 919 |
+
discard <| getElabElimInfo declName
|
| 920 |
+
go.run' {} {}
|
| 921 |
+
|
| 922 |
+
namespace ElabElim
|
| 923 |
+
|
| 924 |
+
/-- Context of the `elab_as_elim` elaboration procedure. -/
|
| 925 |
+
structure Context where
|
| 926 |
+
elimInfo : ElabElimInfo
|
| 927 |
+
expectedType : Expr
|
| 928 |
+
|
| 929 |
+
/-- State of the `elab_as_elim` elaboration procedure. -/
|
| 930 |
+
structure State where
|
| 931 |
+
/-- The resultant expression being built. -/
|
| 932 |
+
f : Expr
|
| 933 |
+
/-- `f : fType -/
|
| 934 |
+
fType : Expr
|
| 935 |
+
/-- User-provided named arguments that still have to be processed. -/
|
| 936 |
+
namedArgs : List NamedArg
|
| 937 |
+
/-- User-provided arguments that still have to be processed. -/
|
| 938 |
+
args : List Arg
|
| 939 |
+
/-- Instance implicit arguments collected so far. -/
|
| 940 |
+
instMVars : Array MVarId := #[]
|
| 941 |
+
/-- Position of the next argument to be processed. We use it to decide whether the argument is the motive or a discriminant. -/
|
| 942 |
+
idx : Nat := 0
|
| 943 |
+
/-- Store the metavariable used to represent the motive that will be computed at `finalize`. -/
|
| 944 |
+
motive? : Option Expr := none
|
| 945 |
+
|
| 946 |
+
abbrev M := ReaderT Context $ StateRefT State TermElabM
|
| 947 |
+
|
| 948 |
+
/-- Infer the `motive` using the expected type by `kabstract`ing the discriminants. -/
|
| 949 |
+
def mkMotive (discrs : Array Expr) (expectedType : Expr) : MetaM Expr := do
|
| 950 |
+
discrs.foldrM (init := expectedType) fun discr motive => do
|
| 951 |
+
let discr β instantiateMVars discr
|
| 952 |
+
let motiveBody β kabstract motive discr
|
| 953 |
+
/- We use `transform (usedLetOnly := true)` to eliminate unnecessary let-expressions. -/
|
| 954 |
+
let discrType β transform (usedLetOnly := true) (β instantiateMVars (β inferType discr))
|
| 955 |
+
return Lean.mkLambda (β mkFreshBinderName) BinderInfo.default discrType motiveBody
|
| 956 |
+
|
| 957 |
+
/--
|
| 958 |
+
If the eliminator is over-applied, we "revert" the extra arguments.
|
| 959 |
+
Returns the function with the reverted arguments applied and the new generalized expected type.
|
| 960 |
+
-/
|
| 961 |
+
def revertArgs (args : List Arg) (f : Expr) (expectedType : Expr) : TermElabM (Expr Γ Expr) := do
|
| 962 |
+
let (xs, expectedType) β args.foldrM (init := ([], expectedType)) fun arg (xs, expectedType) => do
|
| 963 |
+
let val β
|
| 964 |
+
match arg with
|
| 965 |
+
| .expr val => pure val
|
| 966 |
+
| .stx stx => elabTerm stx none
|
| 967 |
+
let val β instantiateMVars val
|
| 968 |
+
let expectedTypeBody β kabstract expectedType val
|
| 969 |
+
/- We use `transform (usedLetOnly := true)` to eliminate unnecessary let-expressions. -/
|
| 970 |
+
let valType β transform (usedLetOnly := true) (β instantiateMVars (β inferType val))
|
| 971 |
+
return (val :: xs, mkForall (β mkFreshBinderName) BinderInfo.default valType expectedTypeBody)
|
| 972 |
+
return (xs.foldl .app f, expectedType)
|
| 973 |
+
|
| 974 |
+
/--
|
| 975 |
+
Construct the resulting application after all discriminants have been elaborated, and we have
|
| 976 |
+
consumed as many given arguments as possible.
|
| 977 |
+
-/
|
| 978 |
+
def finalize : M Expr := do
|
| 979 |
+
unless (β get).namedArgs.isEmpty do
|
| 980 |
+
throwError "failed to elaborate eliminator, unused named arguments: {(β get).namedArgs.map (Β·.name)}"
|
| 981 |
+
let some motive := (β get).motive?
|
| 982 |
+
| throwError "failed to elaborate eliminator, insufficient number of arguments"
|
| 983 |
+
trace[Elab.app.elab_as_elim] "motive: {motive}"
|
| 984 |
+
forallTelescope (β get).fType fun xs fType => do
|
| 985 |
+
trace[Elab.app.elab_as_elim] "xs: {xs}"
|
| 986 |
+
let mut expectedType := (β read).expectedType
|
| 987 |
+
trace[Elab.app.elab_as_elim] "expectedType:{indentD expectedType}"
|
| 988 |
+
let throwInsufficient := do
|
| 989 |
+
throwError "failed to elaborate eliminator, insufficient number of arguments, expected type:{indentExpr expectedType}"
|
| 990 |
+
let mut f := (β get).f
|
| 991 |
+
if xs.size > 0 then
|
| 992 |
+
-- under-application, specialize the expected type using `xs`
|
| 993 |
+
-- Note: if we ever wanted to support optParams and autoParams, this is where it could be.
|
| 994 |
+
assert! (β get).args.isEmpty
|
| 995 |
+
for x in xs do
|
| 996 |
+
let .forallE _ t b _ β whnf expectedType | throwInsufficient
|
| 997 |
+
unless β fullApproxDefEq <| isDefEq t (β inferType x) do
|
| 998 |
+
-- We can't assume that these binding domains were supposed to line up, so report insufficient arguments
|
| 999 |
+
throwInsufficient
|
| 1000 |
+
expectedType := b.instantiate1 x
|
| 1001 |
+
trace[Elab.app.elab_as_elim] "xs after specialization of expected type: {xs}"
|
| 1002 |
+
else
|
| 1003 |
+
-- over-application, simulate `revert` while generalizing the values of these arguments in the expected type
|
| 1004 |
+
(f, expectedType) β revertArgs (β get).args f expectedType
|
| 1005 |
+
unless β isTypeCorrect expectedType do
|
| 1006 |
+
throwError "failed to elaborate eliminator, after generalizing over-applied arguments, expected type is type incorrect:{indentExpr expectedType}"
|
| 1007 |
+
trace[Elab.app.elab_as_elim] "expectedType after processing:{indentD expectedType}"
|
| 1008 |
+
let result := mkAppN f xs
|
| 1009 |
+
trace[Elab.app.elab_as_elim] "result:{indentD result}"
|
| 1010 |
+
unless fType.getAppFn == (β get).motive? do
|
| 1011 |
+
throwError "Internal error, eliminator target type isn't an application of the motive"
|
| 1012 |
+
let discrs := fType.getAppArgs
|
| 1013 |
+
trace[Elab.app.elab_as_elim] "discrs: {discrs}"
|
| 1014 |
+
let motiveVal β mkMotive discrs expectedType
|
| 1015 |
+
unless (β isTypeCorrect motiveVal) do
|
| 1016 |
+
throwError "failed to elaborate eliminator, motive is not type correct:{indentD motiveVal}"
|
| 1017 |
+
unless (β isDefEq motive motiveVal) do
|
| 1018 |
+
throwError "failed to elaborate eliminator, invalid motive{indentExpr motiveVal}"
|
| 1019 |
+
synthesizeAppInstMVars (β get).instMVars result
|
| 1020 |
+
trace[Elab.app.elab_as_elim] "completed motive:{indentD motive}"
|
| 1021 |
+
let result β mkLambdaFVars xs (β instantiateMVars result)
|
| 1022 |
+
return result
|
| 1023 |
+
|
| 1024 |
+
/--
|
| 1025 |
+
Return the next argument to be processed.
|
| 1026 |
+
The result is `.none` if it is an implicit argument which was not provided using a named argument.
|
| 1027 |
+
The result is `.undef` if `args` is empty and `namedArgs` does contain an entry for `binderName`.
|
| 1028 |
+
-/
|
| 1029 |
+
def getNextArg? (binderName : Name) (binderInfo : BinderInfo) : M (LOption Arg) := do
|
| 1030 |
+
match findBinderName? (β get).namedArgs binderName with
|
| 1031 |
+
| some namedArg =>
|
| 1032 |
+
modify fun s => { s with namedArgs := eraseNamedArg s.namedArgs binderName }
|
| 1033 |
+
return .some namedArg.val
|
| 1034 |
+
| none =>
|
| 1035 |
+
if binderInfo.isExplicit then
|
| 1036 |
+
match (β get).args with
|
| 1037 |
+
| [] => return .undef
|
| 1038 |
+
| arg :: args =>
|
| 1039 |
+
modify fun s => { s with args }
|
| 1040 |
+
return .some arg
|
| 1041 |
+
else
|
| 1042 |
+
return .none
|
| 1043 |
+
|
| 1044 |
+
/-- Set the `motive` field in the state. -/
|
| 1045 |
+
def setMotive (motive : Expr) : M Unit :=
|
| 1046 |
+
modify fun s => { s with motive? := motive }
|
| 1047 |
+
|
| 1048 |
+
/-- Elaborate the given argument with the given expected type. -/
|
| 1049 |
+
private def elabArg (arg : Arg) (argExpectedType : Expr) : M Expr := do
|
| 1050 |
+
match arg with
|
| 1051 |
+
| Arg.expr val => ensureArgType (β get).f val argExpectedType
|
| 1052 |
+
| Arg.stx stx =>
|
| 1053 |
+
let val β elabTerm stx argExpectedType
|
| 1054 |
+
withRef stx <| ensureArgType (β get).f val argExpectedType
|
| 1055 |
+
|
| 1056 |
+
/-- Save information for producing error messages. -/
|
| 1057 |
+
def saveArgInfo (arg : Expr) (binderName : Name) : M Unit := do
|
| 1058 |
+
if arg.isMVar then
|
| 1059 |
+
registerMVarArgName arg.mvarId! binderName
|
| 1060 |
+
|
| 1061 |
+
/-- Create an implicit argument using the given `BinderInfo`. -/
|
| 1062 |
+
def mkImplicitArg (argExpectedType : Expr) (bi : BinderInfo) : M Expr := do
|
| 1063 |
+
let arg β mkFreshExprMVar argExpectedType (if bi.isInstImplicit then .synthetic else .natural)
|
| 1064 |
+
if bi.isInstImplicit then
|
| 1065 |
+
modify fun s => { s with instMVars := s.instMVars.push arg.mvarId! }
|
| 1066 |
+
return arg
|
| 1067 |
+
|
| 1068 |
+
/-- Main loop of the `elimAsElab` procedure. -/
|
| 1069 |
+
partial def main : M Expr := do
|
| 1070 |
+
let .forallE binderName binderType body binderInfo β whnfForall (β get).fType |
|
| 1071 |
+
finalize
|
| 1072 |
+
let addArgAndContinue (arg : Expr) : M Expr := do
|
| 1073 |
+
modify fun s => { s with idx := s.idx + 1, f := mkApp s.f arg, fType := body.instantiate1 arg }
|
| 1074 |
+
saveArgInfo arg binderName
|
| 1075 |
+
main
|
| 1076 |
+
let idx := (β get).idx
|
| 1077 |
+
if (β read).elimInfo.motivePos == idx then
|
| 1078 |
+
let motive β
|
| 1079 |
+
match (β getNextArg? binderName binderInfo) with
|
| 1080 |
+
| .some arg =>
|
| 1081 |
+
/- Due to `Lean.Elab.Term.elabAppArgs.elabAsElim?`, this must be a positional argument that is the syntax `_`. -/
|
| 1082 |
+
elabArg arg binderType
|
| 1083 |
+
| .none | .undef =>
|
| 1084 |
+
/- Note: undef occurs when the motive is explicit but missing.
|
| 1085 |
+
In this case, we treat it as if it were an implicit argument
|
| 1086 |
+
to support writing `h.rec` when `h : False`, rather than requiring `h.rec _`. -/
|
| 1087 |
+
mkImplicitArg binderType binderInfo
|
| 1088 |
+
setMotive motive
|
| 1089 |
+
addArgAndContinue motive
|
| 1090 |
+
else if (β read).elimInfo.majorsPos.contains idx then
|
| 1091 |
+
match (β getNextArg? binderName binderInfo) with
|
| 1092 |
+
| .some arg => let discr β elabArg arg binderType; addArgAndContinue discr
|
| 1093 |
+
| .undef => finalize
|
| 1094 |
+
| .none => let discr β mkImplicitArg binderType binderInfo; addArgAndContinue discr
|
| 1095 |
+
else match (β getNextArg? binderName binderInfo) with
|
| 1096 |
+
| .some (.stx stx) => addArgAndContinue (β postponeElabTerm stx binderType)
|
| 1097 |
+
| .some (.expr val) => addArgAndContinue (β ensureArgType (β get).f val binderType)
|
| 1098 |
+
| .undef => finalize
|
| 1099 |
+
| .none => addArgAndContinue (β mkImplicitArg binderType binderInfo)
|
| 1100 |
+
|
| 1101 |
+
end ElabElim
|
| 1102 |
+
|
| 1103 |
+
/-- Return `true` if `declName` is a candidate for `ElabElim.main` elaboration. -/
|
| 1104 |
+
private def shouldElabAsElim (declName : Name) : CoreM Bool := do
|
| 1105 |
+
if (β isRec declName) then return true
|
| 1106 |
+
let env β getEnv
|
| 1107 |
+
if isCasesOnRecursor env declName then return true
|
| 1108 |
+
if isBRecOnRecursor env declName then return true
|
| 1109 |
+
if isRecOnRecursor env declName then return true
|
| 1110 |
+
return elabAsElim.hasTag env declName
|
| 1111 |
+
|
| 1112 |
+
private def propagateExpectedTypeFor (f : Expr) : TermElabM Bool :=
|
| 1113 |
+
match f.getAppFn.constName? with
|
| 1114 |
+
| some declName => return !hasElabWithoutExpectedType (β getEnv) declName
|
| 1115 |
+
| _ => return true
|
| 1116 |
+
|
| 1117 |
+
/-! # Function application elaboration -/
|
| 1118 |
+
|
| 1119 |
+
/--
|
| 1120 |
+
Elaborate a `f`-application using `namedArgs` and `args` as the arguments.
|
| 1121 |
+
- `expectedType?` the expected type if available. It is used to propagate typing information only. This method does **not** ensure the result has this type.
|
| 1122 |
+
- `explicit = true` when notation `@` is used, and implicit arguments are assumed to be provided at `namedArgs` and `args`.
|
| 1123 |
+
- `ellipsis = true` when notation `..` is used. That is, we add `_` for missing arguments.
|
| 1124 |
+
- `resultIsOutParamSupport` is used to control whether special support is used when processing applications of functions that return
|
| 1125 |
+
output parameter of some local instance. Example:
|
| 1126 |
+
```
|
| 1127 |
+
GetElem.getElem : {Cont : Type u_1} β {Idx : Type u_2} β {elem : Type u_3} β {dom : cont β idx β Prop} β [self : GetElem cont idx elem dom] β (xs : cont) β (i : idx) β dom xs i β elem
|
| 1128 |
+
```
|
| 1129 |
+
The result type `elem` is the output parameter of the local instance `self`.
|
| 1130 |
+
When this parameter is set to `true`, we execute `synthesizeSyntheticMVarsUsingDefault`. For additional details, see comment at
|
| 1131 |
+
`ElabAppArgs.resultIsOutParam`.
|
| 1132 |
+
-/
|
| 1133 |
+
def elabAppArgs (f : Expr) (namedArgs : Array NamedArg) (args : Array Arg)
|
| 1134 |
+
(expectedType? : Option Expr) (explicit ellipsis : Bool) (resultIsOutParamSupport := true) : TermElabM Expr := do
|
| 1135 |
+
-- Coercions must be available to use this flag.
|
| 1136 |
+
-- If `@` is used (i.e., `explicit = true`), we disable `resultIsOutParamSupport`.
|
| 1137 |
+
let resultIsOutParamSupport := ((β getEnv).contains ``Lean.Internal.coeM) && resultIsOutParamSupport && !explicit
|
| 1138 |
+
let fType β inferType f
|
| 1139 |
+
let fType β instantiateMVars fType
|
| 1140 |
+
unless namedArgs.isEmpty && args.isEmpty do
|
| 1141 |
+
tryPostponeIfMVar fType
|
| 1142 |
+
trace[Elab.app.args] "explicit: {explicit}, ellipsis: {ellipsis}, {f} : {fType}"
|
| 1143 |
+
trace[Elab.app.args] "namedArgs: {namedArgs}"
|
| 1144 |
+
trace[Elab.app.args] "args: {args}"
|
| 1145 |
+
if let some elimInfo β elabAsElim? then
|
| 1146 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 1147 |
+
let some expectedType := expectedType? | throwError "failed to elaborate eliminator, expected type is not available"
|
| 1148 |
+
let expectedType β instantiateMVars expectedType
|
| 1149 |
+
if expectedType.getAppFn.isMVar then throwError "failed to elaborate eliminator, expected type is not available"
|
| 1150 |
+
ElabElim.main.run { elimInfo, expectedType } |>.run' {
|
| 1151 |
+
f, fType
|
| 1152 |
+
args := args.toList
|
| 1153 |
+
namedArgs := namedArgs.toList
|
| 1154 |
+
}
|
| 1155 |
+
else
|
| 1156 |
+
ElabAppArgs.main.run { explicit, ellipsis, resultIsOutParamSupport } |>.run' {
|
| 1157 |
+
args := args.toList
|
| 1158 |
+
expectedType?, f, fType
|
| 1159 |
+
namedArgs := namedArgs.toList
|
| 1160 |
+
propagateExpected := (β propagateExpectedTypeFor f)
|
| 1161 |
+
}
|
| 1162 |
+
where
|
| 1163 |
+
/-- Return `some info` if we should elaborate as an eliminator. -/
|
| 1164 |
+
elabAsElim? : TermElabM (Option ElabElimInfo) := do
|
| 1165 |
+
unless (β read).heedElabAsElim do return none
|
| 1166 |
+
if explicit || ellipsis then return none
|
| 1167 |
+
let .const declName _ := f | return none
|
| 1168 |
+
unless (β shouldElabAsElim declName) do return none
|
| 1169 |
+
let elimInfo β getElabElimInfo declName
|
| 1170 |
+
forallTelescopeReducing (β inferType f) fun xs _ => do
|
| 1171 |
+
/- Process arguments similar to `Lean.Elab.Term.ElabElim.main` to see if the motive has been
|
| 1172 |
+
provided, in which case we use the standard app elaborator.
|
| 1173 |
+
If the motive is explicit (like for `False.rec`), then a positional `_` counts as "not provided". -/
|
| 1174 |
+
let mut args := args.toList
|
| 1175 |
+
let mut namedArgs := namedArgs.toList
|
| 1176 |
+
for x in xs[*...elimInfo.motivePos] do
|
| 1177 |
+
let localDecl β x.fvarId!.getDecl
|
| 1178 |
+
match findBinderName? namedArgs localDecl.userName with
|
| 1179 |
+
| some _ => namedArgs := eraseNamedArg namedArgs localDecl.userName
|
| 1180 |
+
| none => if localDecl.binderInfo.isExplicit then args := args.tailD []
|
| 1181 |
+
-- Invariant: `elimInfo.motivePos < xs.size` due to construction of `elimInfo`.
|
| 1182 |
+
let some x := xs[elimInfo.motivePos]? | unreachable!
|
| 1183 |
+
let localDecl β x.fvarId!.getDecl
|
| 1184 |
+
if findBinderName? namedArgs localDecl.userName matches some _ then
|
| 1185 |
+
-- motive has been explicitly provided, so we should use standard app elaborator
|
| 1186 |
+
return none
|
| 1187 |
+
else
|
| 1188 |
+
match localDecl.binderInfo.isExplicit, args with
|
| 1189 |
+
| true, .expr _ :: _ =>
|
| 1190 |
+
-- motive has been explicitly provided, so we should use standard app elaborator
|
| 1191 |
+
return none
|
| 1192 |
+
| true, .stx arg :: _ =>
|
| 1193 |
+
if arg.isOfKind ``Lean.Parser.Term.hole then
|
| 1194 |
+
return some elimInfo
|
| 1195 |
+
else
|
| 1196 |
+
-- positional motive is not `_`, so we should use standard app elaborator
|
| 1197 |
+
return none
|
| 1198 |
+
| _, _ => return some elimInfo
|
| 1199 |
+
|
| 1200 |
+
|
| 1201 |
+
/-- Auxiliary inductive datatype that represents the resolution of an `LVal`. -/
|
| 1202 |
+
inductive LValResolution where
|
| 1203 |
+
/-- When applied to `f`, effectively expands to `BaseStruct.fieldName (self := Struct.toBase f)`.
|
| 1204 |
+
This is a special named argument where it suppresses any explicit arguments depending on it so that type parameters don't need to be supplied. -/
|
| 1205 |
+
| projFn (baseStructName : Name) (structName : Name) (fieldName : Name)
|
| 1206 |
+
/-- Similar to `projFn`, but for extracting field indexed by `idx`. Works for structure-like inductive types in general. -/
|
| 1207 |
+
| projIdx (structName : Name) (idx : Nat)
|
| 1208 |
+
/-- When applied to `f`, effectively expands to `constName ... (Struct.toBase f)`, with the argument placed in the correct
|
| 1209 |
+
positional argument if possible, or otherwise as a named argument. The `Struct.toBase` is not present if `baseStructName == structName`,
|
| 1210 |
+
in which case these do not need to be structures. Supports generalized field notation. -/
|
| 1211 |
+
| const (baseStructName : Name) (structName : Name) (constName : Name)
|
| 1212 |
+
/-- Like `const`, but with `fvar` instead of `constName`.
|
| 1213 |
+
The `fullName` is the name of the recursive function, and `baseName` is the base name of the type to search for in the parameter list. -/
|
| 1214 |
+
| localRec (baseName : Name) (fullName : Name) (fvar : Expr)
|
| 1215 |
+
|
| 1216 |
+
private def throwLValErrorAt (ref : Syntax) (e : Expr) (eType : Expr) (msg : MessageData) : TermElabM Ξ± :=
|
| 1217 |
+
throwErrorAt ref "{msg}{indentExpr e}\nhas type{indentExpr eType}"
|
| 1218 |
+
|
| 1219 |
+
private def throwLValError (e : Expr) (eType : Expr) (msg : MessageData) : TermElabM Ξ± := do
|
| 1220 |
+
throwLValErrorAt (β getRef) e eType msg
|
| 1221 |
+
|
| 1222 |
+
/--
|
| 1223 |
+
`findMethod? S fName` tries the for each namespace `S'` in the resolution order for `S` to resolve the name `S'.fname`.
|
| 1224 |
+
If it resolves to `name`, returns `(S', name)`.
|
| 1225 |
+
-/
|
| 1226 |
+
private partial def findMethod? (structName fieldName : Name) : MetaM (Option (Name Γ Name)) := do
|
| 1227 |
+
let env β getEnv
|
| 1228 |
+
let find? structName' : MetaM (Option (Name Γ Name)) := do
|
| 1229 |
+
let fullName := structName' ++ fieldName
|
| 1230 |
+
-- We do not want to make use of the current namespace for resolution.
|
| 1231 |
+
let candidates := ResolveName.resolveGlobalName (β getEnv) Name.anonymous (β getOpenDecls) fullName
|
| 1232 |
+
|>.filter (fun (_, fieldList) => fieldList.isEmpty)
|
| 1233 |
+
|>.map Prod.fst
|
| 1234 |
+
match candidates with
|
| 1235 |
+
| [] => return none
|
| 1236 |
+
| [fullName'] => return some (structName', fullName')
|
| 1237 |
+
| _ =>
|
| 1238 |
+
let candidates := MessageData.joinSep (candidates.map (m!"`{.ofConstName Β·}`")) ", "
|
| 1239 |
+
throwError "Field name `{fieldName}` is ambiguous: `{fullName}` has possible interpretations {candidates}"
|
| 1240 |
+
-- Optimization: the first element of the resolution order is `structName`,
|
| 1241 |
+
-- so we can skip computing the resolution order in the common case
|
| 1242 |
+
-- of the name resolving in the `structName` namespace.
|
| 1243 |
+
find? structName <||> do
|
| 1244 |
+
let resolutionOrder β if isStructure env structName then getStructureResolutionOrder structName else pure #[structName]
|
| 1245 |
+
for ns in resolutionOrder[1...resolutionOrder.size] do
|
| 1246 |
+
if let some res β find? ns then
|
| 1247 |
+
return res
|
| 1248 |
+
return none
|
| 1249 |
+
|
| 1250 |
+
private def throwInvalidFieldNotation (e eType : Expr) : TermElabM Ξ± :=
|
| 1251 |
+
throwLValError e eType "Invalid field notation: Type is not of the form `C ...` where C is a constant"
|
| 1252 |
+
|
| 1253 |
+
private def resolveLValAux (e : Expr) (eType : Expr) (lval : LVal) : TermElabM LValResolution := do
|
| 1254 |
+
if eType.isForall then
|
| 1255 |
+
match lval with
|
| 1256 |
+
| LVal.fieldName _ fieldName suffix? fullRef =>
|
| 1257 |
+
let fullName := Name.str `Function fieldName
|
| 1258 |
+
if (β getEnv).contains fullName then
|
| 1259 |
+
return LValResolution.const `Function `Function fullName
|
| 1260 |
+
else if suffix?.isNone then
|
| 1261 |
+
/- If there's no suffix, this could only have been a field in the `Function` namespace, so
|
| 1262 |
+
we needn't wait to check if this is actually a constant. If `suffix?` is non-`none`, we
|
| 1263 |
+
prefer to throw the "unknown constant" error (because of monad namespaces like `IO` and
|
| 1264 |
+
auxiliary declarations like `mutual_induct`) -/
|
| 1265 |
+
throwLValErrorAt fullRef e eType <| mkUnknownIdentifierMessage m!"Invalid field `{fieldName}`: \
|
| 1266 |
+
The environment does not contain `{Name.str `Function fieldName}`"
|
| 1267 |
+
| .fieldIdx .. =>
|
| 1268 |
+
throwLValError e eType "Invalid projection: Projections cannot be used on functions"
|
| 1269 |
+
else if eType.getAppFn.isMVar then
|
| 1270 |
+
let (kind, name) :=
|
| 1271 |
+
match lval with
|
| 1272 |
+
| .fieldName _ fieldName _ _ => (m!"field notation", m!"field `{fieldName}`")
|
| 1273 |
+
| .fieldIdx _ i => (m!"projection", m!"projection `{i}`")
|
| 1274 |
+
throwError "Invalid {kind}: Type of{indentExpr e}\nis not known; cannot resolve {name}"
|
| 1275 |
+
match eType.getAppFn.constName?, lval with
|
| 1276 |
+
| some structName, LVal.fieldIdx _ idx =>
|
| 1277 |
+
if idx == 0 then
|
| 1278 |
+
throwError "Invalid projection: Index must be greater than 0"
|
| 1279 |
+
let env β getEnv
|
| 1280 |
+
let failK _ := throwLValError e eType "Invalid projection: Expected a value whose type is a structure"
|
| 1281 |
+
matchConstStructure eType.getAppFn failK fun _ _ ctorVal => do
|
| 1282 |
+
let numFields := ctorVal.numFields
|
| 1283 |
+
if idx - 1 < numFields then
|
| 1284 |
+
if isStructure env structName then
|
| 1285 |
+
let fieldNames := getStructureFields env structName
|
| 1286 |
+
return LValResolution.projFn structName structName fieldNames[idx - 1]!
|
| 1287 |
+
else
|
| 1288 |
+
/- `structName` was declared using `inductive` command.
|
| 1289 |
+
So, we don't projection functions for it. Thus, we use `Expr.proj` -/
|
| 1290 |
+
return LValResolution.projIdx structName (idx - 1)
|
| 1291 |
+
else
|
| 1292 |
+
let (fields, bounds) := if numFields == 1 then
|
| 1293 |
+
(m!"field", m!"the only valid index is 1")
|
| 1294 |
+
else
|
| 1295 |
+
(m!"fields", m!"it must be between 1 and {numFields}")
|
| 1296 |
+
throwError m!"Invalid projection: Index `{idx}` is invalid for this structure; {bounds}"
|
| 1297 |
+
++ .note m!"The expression{inlineExpr e}has type{inlineExpr eType}which has only {numFields} {fields}"
|
| 1298 |
+
| some structName, LVal.fieldName _ fieldName _ fullRef =>
|
| 1299 |
+
let env β getEnv
|
| 1300 |
+
if isStructure env structName then
|
| 1301 |
+
if let some baseStructName := findField? env structName (Name.mkSimple fieldName) then
|
| 1302 |
+
return LValResolution.projFn baseStructName structName (Name.mkSimple fieldName)
|
| 1303 |
+
-- Search the local context first
|
| 1304 |
+
let fullName := Name.mkStr structName fieldName
|
| 1305 |
+
for localDecl in (β getLCtx) do
|
| 1306 |
+
if localDecl.isAuxDecl then
|
| 1307 |
+
if let some localDeclFullName := (β getLCtx).auxDeclToFullName.find? localDecl.fvarId then
|
| 1308 |
+
if fullName == (privateToUserName? localDeclFullName).getD localDeclFullName then
|
| 1309 |
+
/- LVal notation is being used to make a "local" recursive call. -/
|
| 1310 |
+
return LValResolution.localRec structName fullName localDecl.toExpr
|
| 1311 |
+
-- Then search the environment
|
| 1312 |
+
if let some (baseStructName, fullName) β findMethod? structName (.mkSimple fieldName) then
|
| 1313 |
+
return LValResolution.const baseStructName structName fullName
|
| 1314 |
+
let msg := mkUnknownIdentifierMessage m!"Invalid field `{fieldName}`: The environment does not contain `{Name.mkStr structName fieldName}`"
|
| 1315 |
+
throwLValErrorAt fullRef e eType msg
|
| 1316 |
+
| none, LVal.fieldName _ _ (some suffix) fullRef =>
|
| 1317 |
+
-- This may be a function constant whose implicit arguments have already been filled in:
|
| 1318 |
+
let c := e.getAppFn
|
| 1319 |
+
if c.isConst then
|
| 1320 |
+
throwUnknownConstantAt fullRef (c.constName! ++ suffix)
|
| 1321 |
+
else
|
| 1322 |
+
throwInvalidFieldNotation e eType
|
| 1323 |
+
| _, _ => throwInvalidFieldNotation e eType
|
| 1324 |
+
|
| 1325 |
+
/-- whnfCore + implicit consumption.
|
| 1326 |
+
Example: given `e` with `eType := {Ξ± : Type} β (fun Ξ² => List Ξ²) Ξ± `, it produces `(e ?m, List ?m)` where `?m` is fresh metavariable. -/
|
| 1327 |
+
private partial def consumeImplicits (stx : Syntax) (e eType : Expr) (hasArgs : Bool) : TermElabM (Expr Γ Expr) := do
|
| 1328 |
+
let eType β whnfCore eType
|
| 1329 |
+
match eType with
|
| 1330 |
+
| .forallE _ d b bi =>
|
| 1331 |
+
if bi.isImplicit || (hasArgs && bi.isStrictImplicit) then
|
| 1332 |
+
let mvar β mkFreshExprMVar d
|
| 1333 |
+
registerMVarErrorHoleInfo mvar.mvarId! stx
|
| 1334 |
+
consumeImplicits stx (mkApp e mvar) (b.instantiate1 mvar) hasArgs
|
| 1335 |
+
else if bi.isInstImplicit then
|
| 1336 |
+
let mvar β mkInstMVar d
|
| 1337 |
+
let r := mkApp e mvar
|
| 1338 |
+
registerMVarErrorImplicitArgInfo mvar.mvarId! stx r
|
| 1339 |
+
consumeImplicits stx r (b.instantiate1 mvar) hasArgs
|
| 1340 |
+
else match d.getOptParamDefault? with
|
| 1341 |
+
| some defVal => consumeImplicits stx (mkApp e defVal) (b.instantiate1 defVal) hasArgs
|
| 1342 |
+
-- TODO: we do not handle autoParams here.
|
| 1343 |
+
| _ => return (e, eType)
|
| 1344 |
+
| _ => return (e, eType)
|
| 1345 |
+
|
| 1346 |
+
private partial def resolveLValLoop (lval : LVal) (e eType : Expr) (previousExceptions : Array Exception) (hasArgs : Bool) : TermElabM (Expr Γ LValResolution) := do
|
| 1347 |
+
let (e, eType) β consumeImplicits lval.getRef e eType hasArgs
|
| 1348 |
+
tryPostponeIfMVar eType
|
| 1349 |
+
/- If `eType` is still a metavariable application, we try to apply default instances to "unblock" it. -/
|
| 1350 |
+
if (β isMVarApp eType) then
|
| 1351 |
+
synthesizeSyntheticMVarsUsingDefault
|
| 1352 |
+
let eType β instantiateMVars eType
|
| 1353 |
+
try
|
| 1354 |
+
let lvalRes β resolveLValAux e eType lval
|
| 1355 |
+
return (e, lvalRes)
|
| 1356 |
+
catch
|
| 1357 |
+
| ex@(Exception.error _ _) =>
|
| 1358 |
+
let eType? β unfoldDefinition? eType
|
| 1359 |
+
match eType? with
|
| 1360 |
+
| some eType => resolveLValLoop lval e eType (previousExceptions.push ex) hasArgs
|
| 1361 |
+
| none =>
|
| 1362 |
+
previousExceptions.forM fun ex => logException ex
|
| 1363 |
+
throw ex
|
| 1364 |
+
| ex@(Exception.internal _ _) => throw ex
|
| 1365 |
+
|
| 1366 |
+
private def resolveLVal (e : Expr) (lval : LVal) (hasArgs : Bool) : TermElabM (Expr Γ LValResolution) := do
|
| 1367 |
+
let eType β inferType e
|
| 1368 |
+
resolveLValLoop lval e eType #[] hasArgs
|
| 1369 |
+
|
| 1370 |
+
private partial def mkBaseProjections (baseStructName : Name) (structName : Name) (e : Expr) : TermElabM Expr := do
|
| 1371 |
+
let env β getEnv
|
| 1372 |
+
match getPathToBaseStructure? env baseStructName structName with
|
| 1373 |
+
| none => throwError "Internal error: Failed to access field in parent structure"
|
| 1374 |
+
| some path =>
|
| 1375 |
+
let mut e := e
|
| 1376 |
+
for projFunName in path do
|
| 1377 |
+
let projFn β mkConst projFunName
|
| 1378 |
+
e β elabAppArgs projFn #[{ name := `self, val := Arg.expr e, suppressDeps := true }] (args := #[]) (expectedType? := none) (explicit := false) (ellipsis := false)
|
| 1379 |
+
return e
|
| 1380 |
+
|
| 1381 |
+
private partial def typeMatchesBaseName (type : Expr) (baseName : Name) : MetaM Bool :=
|
| 1382 |
+
withReducibleAndInstances do
|
| 1383 |
+
if baseName == `Function then
|
| 1384 |
+
return (β whnf type).isForall
|
| 1385 |
+
else if type.cleanupAnnotations.isAppOf baseName then
|
| 1386 |
+
return true
|
| 1387 |
+
else
|
| 1388 |
+
let type β whnfCore type
|
| 1389 |
+
if type.isAppOf baseName then
|
| 1390 |
+
return true
|
| 1391 |
+
else
|
| 1392 |
+
match β unfoldDefinition? type with
|
| 1393 |
+
| some type' => typeMatchesBaseName type' baseName
|
| 1394 |
+
| none => return false
|
| 1395 |
+
|
| 1396 |
+
/--
|
| 1397 |
+
Auxiliary method for field notation. Tries to add `e` as a new argument to `args` or `namedArgs`.
|
| 1398 |
+
This method first finds the parameter with a type of the form `(baseName ...)`.
|
| 1399 |
+
When the parameter is found, if it an explicit one and `args` is big enough, we add `e` to `args`.
|
| 1400 |
+
Otherwise, if there isn't another parameter with the same name, we add `e` to `namedArgs`.
|
| 1401 |
+
|
| 1402 |
+
Remark: `fullName` is the name of the resolved "field" access function. It is used for reporting errors
|
| 1403 |
+
-/
|
| 1404 |
+
private partial def addLValArg (baseName : Name) (fullName : Name) (e : Expr) (args : Array Arg) (namedArgs : Array NamedArg) (f : Expr) (explicit : Bool) :
|
| 1405 |
+
MetaM (Array Arg Γ Array NamedArg) := do
|
| 1406 |
+
withoutModifyingState <| go f (β inferType f) 0 namedArgs (namedArgs.map (Β·.name)) true
|
| 1407 |
+
where
|
| 1408 |
+
/--
|
| 1409 |
+
* `argIdx` is the position into `args` for the next place an explicit argument can be inserted.
|
| 1410 |
+
* `remainingNamedArgs` keeps track of named arguments that haven't been visited yet,
|
| 1411 |
+
for handling the case where multiple parameters have the same name.
|
| 1412 |
+
* `unusableNamedArgs` keeps track of names that can't be used as named arguments. This is initialized with user-provided named arguments.
|
| 1413 |
+
* `allowNamed` is whether or not to allow using named arguments.
|
| 1414 |
+
Disabled after using `CoeFun` since those parameter names unlikely to be meaningful,
|
| 1415 |
+
and otherwise whether dot notation works or not could feel random.
|
| 1416 |
+
-/
|
| 1417 |
+
go (f fType : Expr) (argIdx : Nat) (remainingNamedArgs : Array NamedArg) (unusableNamedArgs : Array Name) (allowNamed : Bool) := withIncRecDepth do
|
| 1418 |
+
/- Use metavariables (rather than `forallTelescope`) to prevent `coerceToFunction?` from succeeding when multiple instances could apply -/
|
| 1419 |
+
let (xs, bInfos, fType') β forallMetaTelescope fType
|
| 1420 |
+
let mut argIdx := argIdx
|
| 1421 |
+
let mut remainingNamedArgs := remainingNamedArgs
|
| 1422 |
+
let mut unusableNamedArgs := unusableNamedArgs
|
| 1423 |
+
for x in xs, bInfo in bInfos do
|
| 1424 |
+
let xDecl β x.mvarId!.getDecl
|
| 1425 |
+
if let some idx := remainingNamedArgs.findFinIdx? (Β·.name == xDecl.userName) then
|
| 1426 |
+
/- If there is named argument with name `xDecl.userName`, then it is accounted for and we can't make use of it. -/
|
| 1427 |
+
remainingNamedArgs := remainingNamedArgs.eraseIdx idx
|
| 1428 |
+
else
|
| 1429 |
+
if β typeMatchesBaseName xDecl.type baseName then
|
| 1430 |
+
/- We found a type of the form (baseName ...), or we found the first explicit argument in useFirstExplicit mode.
|
| 1431 |
+
First, we check if the current argument is one that can be used positionally,
|
| 1432 |
+
and if the current explicit position "fits" at `args` (i.e., it must be β€ arg.size) -/
|
| 1433 |
+
if h : argIdx β€ args.size β§ (explicit || bInfo.isExplicit) then
|
| 1434 |
+
/- We can insert `e` as an explicit argument -/
|
| 1435 |
+
return (args.insertIdx argIdx (Arg.expr e), namedArgs)
|
| 1436 |
+
else
|
| 1437 |
+
/- If we can't add `e` to `args`, we try to add it using a named argument, but this is only possible
|
| 1438 |
+
if there isn't an argument with the same name occurring before it. -/
|
| 1439 |
+
if !allowNamed || unusableNamedArgs.contains xDecl.userName then
|
| 1440 |
+
throwUnusableParameter allowNamed xDecl
|
| 1441 |
+
else
|
| 1442 |
+
return (args, namedArgs.push { name := xDecl.userName, val := Arg.expr e })
|
| 1443 |
+
/- Advance `argIdx` and update seen named arguments. -/
|
| 1444 |
+
if explicit || bInfo.isExplicit then
|
| 1445 |
+
argIdx := argIdx + 1
|
| 1446 |
+
unusableNamedArgs := unusableNamedArgs.push xDecl.userName
|
| 1447 |
+
/- If named arguments aren't allowed, then it must still be possible to pass the value as an explicit argument.
|
| 1448 |
+
Otherwise, we can abort now. -/
|
| 1449 |
+
if allowNamed || argIdx β€ args.size then
|
| 1450 |
+
if let fType'@(.forallE ..) β whnf fType' then
|
| 1451 |
+
return β go (mkAppN f xs) fType' argIdx remainingNamedArgs unusableNamedArgs allowNamed
|
| 1452 |
+
if let some f' β coerceToFunction? (mkAppN f xs) then
|
| 1453 |
+
return β go f' (β inferType f') argIdx remainingNamedArgs unusableNamedArgs false
|
| 1454 |
+
let tyCtorMsg := MessageData.ofLazyM do
|
| 1455 |
+
let some decl := (β getEnv).find? baseName | return .ofConstName baseName
|
| 1456 |
+
if decl.type.isForall then
|
| 1457 |
+
return m!"{.ofConstName baseName} ..."
|
| 1458 |
+
else
|
| 1459 |
+
return .ofConstName baseName
|
| 1460 |
+
throwError m!"Invalid field notation: Function `{.ofConstName fullName}` does not have a usable \
|
| 1461 |
+
parameter of type `{tyCtorMsg}` for which to substitute{inlineExprTrailing e}"
|
| 1462 |
+
++ .note m!"Such a parameter must be explicit, or implicit with a unique name, to be used by field notation"
|
| 1463 |
+
|
| 1464 |
+
throwUnusableParameter (allowNamed : Bool) (xDecl : MetavarDecl) :=
|
| 1465 |
+
let note : MessageData := if !allowNamed && !xDecl.userName.hasMacroScopes then
|
| 1466 |
+
.note m!"Field notation cannot refer to parameter `{xDecl.userName}` of `{.ofConstName fullName}` \
|
| 1467 |
+
by name because that constant was coerced to a function"
|
| 1468 |
+
else if allowNamed then
|
| 1469 |
+
let param := if xDecl.userName.hasMacroScopes then .nil else m!" `{xDecl.userName}`"
|
| 1470 |
+
.note m!"The parameter{param} of `{.ofConstName fullName}` cannot be referred to by name \
|
| 1471 |
+
because that function has a preceding parameter of the same name"
|
| 1472 |
+
else .nil
|
| 1473 |
+
-- Transforming field notation into direct application is too involved to offer a confident
|
| 1474 |
+
-- concrete edit suggestion here
|
| 1475 |
+
let hint := MessageData.hint' <|
|
| 1476 |
+
m!"Consider rewriting this application without field notation (e.g., `C.f x` instead of `x.f`)" ++
|
| 1477 |
+
if allowNamed then
|
| 1478 |
+
m!" or changing the parameter names of `{.ofConstName fullName}` to avoid this conflict"
|
| 1479 |
+
else .nil
|
| 1480 |
+
throwError m!"Invalid field notation: `{.ofConstName fullName}` has a parameter with \
|
| 1481 |
+
expected type{indentExpr xDecl.type}\nbut it cannot be used" ++ note ++ hint
|
| 1482 |
+
|
| 1483 |
+
/-- Adds the `TermInfo` for the field of a projection. See `Lean.Parser.Term.identProjKind`. -/
|
| 1484 |
+
private def addProjTermInfo
|
| 1485 |
+
(stx : Syntax)
|
| 1486 |
+
(e : Expr)
|
| 1487 |
+
(expectedType? : Option Expr := none)
|
| 1488 |
+
(lctx? : Option LocalContext := none)
|
| 1489 |
+
(elaborator : Name := Name.anonymous)
|
| 1490 |
+
(isBinder force : Bool := false)
|
| 1491 |
+
: TermElabM Expr :=
|
| 1492 |
+
addTermInfo (Syntax.node .none Parser.Term.identProjKind #[stx]) e expectedType? lctx? elaborator isBinder force
|
| 1493 |
+
|
| 1494 |
+
private def elabAppLValsAux (namedArgs : Array NamedArg) (args : Array Arg) (expectedType? : Option Expr) (explicit ellipsis : Bool)
|
| 1495 |
+
(f : Expr) (lvals : List LVal) : TermElabM Expr :=
|
| 1496 |
+
let rec loop : Expr β List LVal β TermElabM Expr
|
| 1497 |
+
| f, [] => elabAppArgs f namedArgs args expectedType? explicit ellipsis
|
| 1498 |
+
| f, lval::lvals => do
|
| 1499 |
+
if let LVal.fieldName (ref := ref) .. := lval then
|
| 1500 |
+
addDotCompletionInfo ref f expectedType?
|
| 1501 |
+
let hasArgs := !namedArgs.isEmpty || !args.isEmpty
|
| 1502 |
+
let (f, lvalRes) β resolveLVal f lval hasArgs
|
| 1503 |
+
match lvalRes with
|
| 1504 |
+
| LValResolution.projIdx structName idx =>
|
| 1505 |
+
let f β mkProjAndCheck structName idx f
|
| 1506 |
+
let f β addTermInfo lval.getRef f
|
| 1507 |
+
loop f lvals
|
| 1508 |
+
| LValResolution.projFn baseStructName structName fieldName =>
|
| 1509 |
+
let f β mkBaseProjections baseStructName structName f
|
| 1510 |
+
let some info := getFieldInfo? (β getEnv) baseStructName fieldName | unreachable!
|
| 1511 |
+
if isPrivateNameFromImportedModule (β getEnv) info.projFn then
|
| 1512 |
+
throwError "Field `{fieldName}` from structure `{structName}` is private"
|
| 1513 |
+
let projFn β mkConst info.projFn
|
| 1514 |
+
let projFn β addProjTermInfo lval.getRef projFn
|
| 1515 |
+
if lvals.isEmpty then
|
| 1516 |
+
let namedArgs β addNamedArg namedArgs { name := `self, val := Arg.expr f, suppressDeps := true }
|
| 1517 |
+
elabAppArgs projFn namedArgs args expectedType? explicit ellipsis
|
| 1518 |
+
else
|
| 1519 |
+
let f β elabAppArgs projFn #[{ name := `self, val := Arg.expr f, suppressDeps := true }] #[] (expectedType? := none) (explicit := false) (ellipsis := false)
|
| 1520 |
+
loop f lvals
|
| 1521 |
+
| LValResolution.const baseStructName structName constName =>
|
| 1522 |
+
let f β if baseStructName != structName then mkBaseProjections baseStructName structName f else pure f
|
| 1523 |
+
let projFn β mkConst constName
|
| 1524 |
+
let projFn β addProjTermInfo lval.getRef projFn
|
| 1525 |
+
if lvals.isEmpty then
|
| 1526 |
+
let (args, namedArgs) β addLValArg baseStructName constName f args namedArgs projFn explicit
|
| 1527 |
+
elabAppArgs projFn namedArgs args expectedType? explicit ellipsis
|
| 1528 |
+
else
|
| 1529 |
+
let (args, namedArgs) β addLValArg baseStructName constName f #[] #[] projFn (explicit := false)
|
| 1530 |
+
let f β elabAppArgs projFn namedArgs args (expectedType? := none) (explicit := false) (ellipsis := false)
|
| 1531 |
+
loop f lvals
|
| 1532 |
+
| LValResolution.localRec baseName fullName fvar =>
|
| 1533 |
+
let fvar β addProjTermInfo lval.getRef fvar
|
| 1534 |
+
if lvals.isEmpty then
|
| 1535 |
+
let (args, namedArgs) β addLValArg baseName fullName f args namedArgs fvar explicit
|
| 1536 |
+
elabAppArgs fvar namedArgs args expectedType? explicit ellipsis
|
| 1537 |
+
else
|
| 1538 |
+
let (args, namedArgs) β addLValArg baseName fullName f #[] #[] fvar (explicit := false)
|
| 1539 |
+
let f β elabAppArgs fvar namedArgs args (expectedType? := none) (explicit := false) (ellipsis := false)
|
| 1540 |
+
loop f lvals
|
| 1541 |
+
loop f lvals
|
| 1542 |
+
|
| 1543 |
+
private def elabAppLVals (f : Expr) (lvals : List LVal) (namedArgs : Array NamedArg) (args : Array Arg)
|
| 1544 |
+
(expectedType? : Option Expr) (explicit ellipsis : Bool) : TermElabM Expr := do
|
| 1545 |
+
elabAppLValsAux namedArgs args expectedType? explicit ellipsis f lvals
|
| 1546 |
+
|
| 1547 |
+
def elabExplicitUnivs (lvls : Array Syntax) : TermElabM (List Level) := do
|
| 1548 |
+
lvls.foldrM (init := []) fun stx lvls => return (β elabLevel stx)::lvls
|
| 1549 |
+
|
| 1550 |
+
/-!
|
| 1551 |
+
# Interaction between `errToSorry` and `observing`.
|
| 1552 |
+
|
| 1553 |
+
- The method `elabTerm` catches exceptions, logs them, and returns a synthetic sorry (IF `ctx.errToSorry` == true).
|
| 1554 |
+
|
| 1555 |
+
- When we elaborate choice nodes (and overloaded identifiers), we track multiple results using the `observing x` combinator.
|
| 1556 |
+
The `observing x` executes `x` and returns a `TermElabResult`.
|
| 1557 |
+
|
| 1558 |
+
`observing x` does not check for synthetic sorry's, just an exception. Thus, it may think `x` worked when it didn't
|
| 1559 |
+
if a synthetic sorry was introduced. We decided that checking for synthetic sorrys at `observing` is not a good solution
|
| 1560 |
+
because it would not be clear to decide what the "main" error message for the alternative is. When the result contains
|
| 1561 |
+
a synthetic `sorry`, it is not clear which error message corresponds to the `sorry`. Moreover, while executing `x`, many
|
| 1562 |
+
error messages may have been logged. Recall that we need an error per alternative at `mergeFailures`.
|
| 1563 |
+
|
| 1564 |
+
Thus, we decided to set `errToSorry` to `false` whenever processing choice nodes and overloaded symbols.
|
| 1565 |
+
|
| 1566 |
+
Important: we rely on the property that after `errToSorry` is set to
|
| 1567 |
+
false, no elaboration function executed by `x` will reset it to
|
| 1568 |
+
`true`.
|
| 1569 |
+
-/
|
| 1570 |
+
|
| 1571 |
+
private partial def elabAppFnId (fIdent : Syntax) (fExplicitUnivs : List Level) (lvals : List LVal)
|
| 1572 |
+
(namedArgs : Array NamedArg) (args : Array Arg) (expectedType? : Option Expr) (explicit ellipsis overloaded : Bool) (acc : Array (TermElabResult Expr))
|
| 1573 |
+
: TermElabM (Array (TermElabResult Expr)) := do
|
| 1574 |
+
let funLVals β withRef fIdent <| resolveName' fIdent fExplicitUnivs expectedType?
|
| 1575 |
+
let overloaded := overloaded || funLVals.length > 1
|
| 1576 |
+
-- Set `errToSorry` to `false` if `funLVals` > 1. See comment above about the interaction between `errToSorry` and `observing`.
|
| 1577 |
+
withReader (fun ctx => { ctx with errToSorry := funLVals.length == 1 && ctx.errToSorry }) do
|
| 1578 |
+
funLVals.foldlM (init := acc) fun acc (f, fIdent, fields) => do
|
| 1579 |
+
let lvals' := toLVals fields (first := true)
|
| 1580 |
+
let s β observing do
|
| 1581 |
+
checkDeprecated fIdent f
|
| 1582 |
+
let f β addTermInfo fIdent f expectedType?
|
| 1583 |
+
let e β elabAppLVals f (lvals' ++ lvals) namedArgs args expectedType? explicit ellipsis
|
| 1584 |
+
if overloaded then ensureHasType expectedType? e else return e
|
| 1585 |
+
return acc.push s
|
| 1586 |
+
where
|
| 1587 |
+
toName (fields : List Syntax) : Name :=
|
| 1588 |
+
let rec go
|
| 1589 |
+
| [] => .anonymous
|
| 1590 |
+
| field :: fields => .mkStr (go fields) field.getId.toString
|
| 1591 |
+
go fields.reverse
|
| 1592 |
+
|
| 1593 |
+
toLVals : List Syntax β (first : Bool) β List LVal
|
| 1594 |
+
| [], _ => []
|
| 1595 |
+
| field::fields, true => .fieldName field field.getId.getString! (toName (field::fields)) fIdent :: toLVals fields false
|
| 1596 |
+
| field::fields, false => .fieldName field field.getId.getString! none fIdent :: toLVals fields false
|
| 1597 |
+
|
| 1598 |
+
/-- Resolve `(.$id:ident)` using the expected type to infer namespace. -/
|
| 1599 |
+
private partial def resolveDotName (id : Syntax) (expectedType? : Option Expr) : TermElabM Expr := do
|
| 1600 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 1601 |
+
let some expectedType := expectedType?
|
| 1602 |
+
| throwError "Invalid dotted identifier notation: Could not determine the expected type of `.{id}`"
|
| 1603 |
+
withForallBody expectedType fun resultType => do
|
| 1604 |
+
go resultType expectedType #[]
|
| 1605 |
+
where
|
| 1606 |
+
/-- A weak version of forallTelescopeReducing that only uses whnfCore, to avoid unfolding definitions except by `unfoldDefinition?` below. -/
|
| 1607 |
+
withForallBody {Ξ±} (type : Expr) (k : Expr β TermElabM Ξ±) : TermElabM Ξ± :=
|
| 1608 |
+
forallTelescope type fun _ body => do
|
| 1609 |
+
let body β whnfCore body
|
| 1610 |
+
if body.isForall then
|
| 1611 |
+
withForallBody body k
|
| 1612 |
+
else
|
| 1613 |
+
k body
|
| 1614 |
+
go (resultType : Expr) (expectedType : Expr) (previousExceptions : Array Exception) : TermElabM Expr := do
|
| 1615 |
+
let resultType β instantiateMVars resultType
|
| 1616 |
+
let resultTypeFn := resultType.cleanupAnnotations.getAppFn
|
| 1617 |
+
try
|
| 1618 |
+
tryPostponeIfMVar resultTypeFn
|
| 1619 |
+
match resultTypeFn.cleanupAnnotations with
|
| 1620 |
+
| .const declName .. =>
|
| 1621 |
+
let idNew := declName ++ id.getId.eraseMacroScopes
|
| 1622 |
+
if (β getEnv).contains idNew then
|
| 1623 |
+
mkConst idNew
|
| 1624 |
+
else if let some (fvar, []) β resolveLocalName idNew then
|
| 1625 |
+
return fvar
|
| 1626 |
+
else
|
| 1627 |
+
throwUnknownIdentifierAt id <| m!"Unknown identifier `{idNew}`"
|
| 1628 |
+
++ .note m!"Inferred this identifier from the expected type of `.{id}`:{indentExpr expectedType}"
|
| 1629 |
+
| .sort .. =>
|
| 1630 |
+
throwNamedError lean.invalidDottedIdent "Invalid dotted identifier notation: Not supported on type universe{indentExpr resultTypeFn}"
|
| 1631 |
+
| _ =>
|
| 1632 |
+
if expectedType.getAppFn.isMVar then
|
| 1633 |
+
throwNamedError lean.invalidDottedIdent "Invalid dotted identifier notation: The expected type of `.{id}` could not be determined"
|
| 1634 |
+
else
|
| 1635 |
+
throwNamedError lean.invalidDottedIdent "Invalid dotted identifier notation: The expected type of `.{id}`{indentExpr expectedType}\n\
|
| 1636 |
+
is not of the form `C ...` or `... β C ...` where C is a constant"
|
| 1637 |
+
catch
|
| 1638 |
+
| ex@(.error ..) =>
|
| 1639 |
+
match (β unfoldDefinition? resultType) with
|
| 1640 |
+
| some resultType =>
|
| 1641 |
+
withForallBody resultType fun resultType => do
|
| 1642 |
+
go resultType expectedType (previousExceptions.push ex)
|
| 1643 |
+
| none =>
|
| 1644 |
+
previousExceptions.forM fun ex => logException ex
|
| 1645 |
+
throw ex
|
| 1646 |
+
| ex@(.internal _ _) => throw ex
|
| 1647 |
+
|
| 1648 |
+
private partial def elabAppFn (f : Syntax) (lvals : List LVal) (namedArgs : Array NamedArg) (args : Array Arg)
|
| 1649 |
+
(expectedType? : Option Expr) (explicit ellipsis overloaded : Bool) (acc : Array (TermElabResult Expr)) : TermElabM (Array (TermElabResult Expr)) := do
|
| 1650 |
+
if f.getKind == choiceKind then
|
| 1651 |
+
-- Set `errToSorry` to `false` when processing choice nodes. See comment above about the interaction between `errToSorry` and `observing`.
|
| 1652 |
+
withReader (fun ctx => { ctx with errToSorry := false }) do
|
| 1653 |
+
f.getArgs.foldlM (init := acc) fun acc f => elabAppFn f lvals namedArgs args expectedType? explicit ellipsis true acc
|
| 1654 |
+
else
|
| 1655 |
+
let elabFieldName (e field : Syntax) (explicit : Bool) := do
|
| 1656 |
+
let newLVals := field.identComponents.map fun comp =>
|
| 1657 |
+
-- We use `none` in `suffix?` since `field` can't be part of a composite name
|
| 1658 |
+
LVal.fieldName comp comp.getId.getString! none f
|
| 1659 |
+
elabAppFn e (newLVals ++ lvals) namedArgs args expectedType? explicit ellipsis overloaded acc
|
| 1660 |
+
let elabFieldIdx (e idxStx : Syntax) (explicit : Bool) := do
|
| 1661 |
+
let some idx := idxStx.isFieldIdx?
|
| 1662 |
+
| throwError "Internal error: Unexpected field index syntax `{idxStx}`"
|
| 1663 |
+
elabAppFn e (LVal.fieldIdx idxStx idx :: lvals) namedArgs args expectedType? explicit ellipsis overloaded acc
|
| 1664 |
+
match f with
|
| 1665 |
+
| `($(e).$idx:fieldIdx) => elabFieldIdx e idx explicit
|
| 1666 |
+
| `($e |>.$idx:fieldIdx) => elabFieldIdx e idx explicit
|
| 1667 |
+
| `($(e).$field:ident) => elabFieldName e field explicit
|
| 1668 |
+
| `($e |>.$field:ident) => elabFieldName e field explicit
|
| 1669 |
+
| `(@$(e).$idx:fieldIdx) => elabFieldIdx e idx (explicit := true)
|
| 1670 |
+
| `(@$(e).$field:ident) => elabFieldName e field (explicit := true)
|
| 1671 |
+
| `($_:ident@$_:term) =>
|
| 1672 |
+
throwError m!"Expected a function, but found the named pattern{indentD f}"
|
| 1673 |
+
++ .note m!"Named patterns `<identifier>@<term>` can only be used when pattern-matching"
|
| 1674 |
+
| `($id:ident) => do
|
| 1675 |
+
elabAppFnId id [] lvals namedArgs args expectedType? explicit ellipsis overloaded acc
|
| 1676 |
+
| `($id:ident.{$us,*}) => do
|
| 1677 |
+
let us β elabExplicitUnivs us
|
| 1678 |
+
elabAppFnId id us lvals namedArgs args expectedType? explicit ellipsis overloaded acc
|
| 1679 |
+
| `(@$id:ident) =>
|
| 1680 |
+
elabAppFn id lvals namedArgs args expectedType? (explicit := true) ellipsis overloaded acc
|
| 1681 |
+
| `(@$_:ident.{$_us,*}) =>
|
| 1682 |
+
elabAppFn (f.getArg 1) lvals namedArgs args expectedType? (explicit := true) ellipsis overloaded acc
|
| 1683 |
+
| `(@$_) => throwUnsupportedSyntax -- invalid occurrence of `@`
|
| 1684 |
+
| `(_) => throwError "A placeholder `_` cannot be used where a function is expected"
|
| 1685 |
+
| `(.$id:ident) =>
|
| 1686 |
+
addCompletionInfo <| CompletionInfo.dotId id id.getId (β getLCtx) expectedType?
|
| 1687 |
+
let fConst β resolveDotName id expectedType?
|
| 1688 |
+
let s β observing do
|
| 1689 |
+
-- Use (force := true) because we want to record the result of .ident resolution even in patterns
|
| 1690 |
+
let fConst β addTermInfo f fConst expectedType? (force := true)
|
| 1691 |
+
let e β elabAppLVals fConst lvals namedArgs args expectedType? explicit ellipsis
|
| 1692 |
+
if overloaded then ensureHasType expectedType? e else return e
|
| 1693 |
+
return acc.push s
|
| 1694 |
+
| _ => do
|
| 1695 |
+
let catchPostpone := !overloaded
|
| 1696 |
+
/- If we are processing a choice node, then we should use `catchPostpone == false` when elaborating terms.
|
| 1697 |
+
Recall that `observing` does not catch `postponeExceptionId`. -/
|
| 1698 |
+
if lvals.isEmpty && namedArgs.isEmpty && args.isEmpty then
|
| 1699 |
+
/- Recall that elabAppFn is used for elaborating atomics terms **and** choice nodes that may contain
|
| 1700 |
+
arbitrary terms. If they are not being used as a function, we should elaborate using the expectedType. -/
|
| 1701 |
+
let s β observing do
|
| 1702 |
+
if overloaded then
|
| 1703 |
+
elabTermEnsuringType f expectedType? catchPostpone
|
| 1704 |
+
else
|
| 1705 |
+
elabTerm f expectedType?
|
| 1706 |
+
return acc.push s
|
| 1707 |
+
else
|
| 1708 |
+
let s β observing do
|
| 1709 |
+
let f β elabTerm f none catchPostpone
|
| 1710 |
+
let e β elabAppLVals f lvals namedArgs args expectedType? explicit ellipsis
|
| 1711 |
+
if overloaded then ensureHasType expectedType? e else return e
|
| 1712 |
+
return acc.push s
|
| 1713 |
+
|
| 1714 |
+
/-- Return the successful candidates. Recall we have Syntax `choice` nodes and overloaded symbols when we open multiple namespaces. -/
|
| 1715 |
+
private def getSuccesses (candidates : Array (TermElabResult Expr)) : TermElabM (Array (TermElabResult Expr)) := do
|
| 1716 |
+
let rβ := candidates.filter fun | EStateM.Result.ok .. => true | _ => false
|
| 1717 |
+
if rβ.size β€ 1 then return rβ
|
| 1718 |
+
let rβ β candidates.filterM fun
|
| 1719 |
+
| .ok e s => do
|
| 1720 |
+
if e.isMVar then
|
| 1721 |
+
/- Make sure `e` is not a delayed coercion.
|
| 1722 |
+
Recall that coercion insertion may be delayed when the type and expected type contains
|
| 1723 |
+
metavariables that block TC resolution.
|
| 1724 |
+
When processing overloaded notation, we disallow delayed coercions at `e`. -/
|
| 1725 |
+
try
|
| 1726 |
+
s.restore
|
| 1727 |
+
synthesizeSyntheticMVars -- Tries to process pending coercions (and elaboration tasks)
|
| 1728 |
+
let e β instantiateMVars e
|
| 1729 |
+
if e.isMVar then
|
| 1730 |
+
/- If `e` is still a metavariable, and its `SyntheticMVarDecl` is a coercion, we discard this solution -/
|
| 1731 |
+
if let some synDecl β getSyntheticMVarDecl? e.mvarId! then
|
| 1732 |
+
if synDecl.kind matches SyntheticMVarKind.coe .. then
|
| 1733 |
+
return false
|
| 1734 |
+
catch _ =>
|
| 1735 |
+
-- If `synthesizeSyntheticMVars` failed, we just eliminate the candidate.
|
| 1736 |
+
return false
|
| 1737 |
+
return true
|
| 1738 |
+
| _ => return false
|
| 1739 |
+
if rβ.size == 0 then
|
| 1740 |
+
return rβ
|
| 1741 |
+
if rβ.size == 1 then
|
| 1742 |
+
return rβ
|
| 1743 |
+
/-
|
| 1744 |
+
If there are still more than one solution, discard solutions that have pending metavariables.
|
| 1745 |
+
We added this extra filter to address regressions introduced after fixing
|
| 1746 |
+
`isDefEqStuckEx` behavior at `ExprDefEq.lean`.
|
| 1747 |
+
-/
|
| 1748 |
+
let rβ β candidates.filterM fun
|
| 1749 |
+
| .ok _ s => do
|
| 1750 |
+
try
|
| 1751 |
+
s.restore
|
| 1752 |
+
synthesizeSyntheticMVars (postpone := .no)
|
| 1753 |
+
return true
|
| 1754 |
+
catch _ =>
|
| 1755 |
+
return false
|
| 1756 |
+
| _ => return false
|
| 1757 |
+
if rβ.size == 0 then
|
| 1758 |
+
return rβ
|
| 1759 |
+
return rβ
|
| 1760 |
+
/--
|
| 1761 |
+
Throw an error message that describes why each possible interpretation for the overloaded notation and symbols did not work.
|
| 1762 |
+
We use a nested error message to aggregate the exceptions produced by each failure.
|
| 1763 |
+
-/
|
| 1764 |
+
private def mergeFailures (failures : Array (TermElabResult Expr)) : TermElabM Ξ± := do
|
| 1765 |
+
let exs := failures.map fun | .error ex _ => ex | _ => unreachable!
|
| 1766 |
+
let trees := failures.map (fun | .error _ s => s.meta.core.infoState.trees | _ => unreachable!)
|
| 1767 |
+
|>.filterMap (Β·[0]?)
|
| 1768 |
+
-- Retain partial `InfoTree` subtrees in an `.ofChoiceInfo` node in case of multiple failures.
|
| 1769 |
+
-- This ensures that the language server still has `Info` to work with when multiple overloaded
|
| 1770 |
+
-- elaborators fail.
|
| 1771 |
+
withInfoContext (mkInfo := pure <| .ofChoiceInfo { elaborator := .anonymous, stx := β getRef }) do
|
| 1772 |
+
for tree in trees do
|
| 1773 |
+
pushInfoTree tree
|
| 1774 |
+
throwErrorWithNestedErrors "overloaded" exs
|
| 1775 |
+
|
| 1776 |
+
private def elabAppAux (f : Syntax) (namedArgs : Array NamedArg) (args : Array Arg) (ellipsis : Bool) (expectedType? : Option Expr) : TermElabM Expr := do
|
| 1777 |
+
let candidates β elabAppFn f [] namedArgs args expectedType? (explicit := false) (ellipsis := ellipsis) (overloaded := false) #[]
|
| 1778 |
+
if h : candidates.size = 1 then
|
| 1779 |
+
have : 0 < candidates.size := by rw [h]; decide
|
| 1780 |
+
applyResult candidates[0]
|
| 1781 |
+
else
|
| 1782 |
+
let successes β getSuccesses candidates
|
| 1783 |
+
if h : successes.size = 1 then
|
| 1784 |
+
have : 0 < successes.size := by rw [h]; decide
|
| 1785 |
+
applyResult successes[0]
|
| 1786 |
+
else if successes.size > 1 then
|
| 1787 |
+
let msgs : Array MessageData β successes.mapM fun success => do
|
| 1788 |
+
match success with
|
| 1789 |
+
| .ok e s => withMCtx s.meta.meta.mctx <| withEnv s.meta.core.env do addMessageContext m!"{e} : {β inferType e}"
|
| 1790 |
+
| _ => unreachable!
|
| 1791 |
+
throwErrorAt f "Ambiguous term{indentD f}\nPossible interpretations:{toMessageList msgs}"
|
| 1792 |
+
else
|
| 1793 |
+
withRef f <| mergeFailures candidates
|
| 1794 |
+
|
| 1795 |
+
/--
|
| 1796 |
+
We annotate recursive applications with their `Syntax` node to make sure we can produce error messages with
|
| 1797 |
+
correct position information at `WF` and `Structural`.
|
| 1798 |
+
-/
|
| 1799 |
+
-- TODO: It is overkill to store the whole `Syntax` object, and we have to make sure we erase it later.
|
| 1800 |
+
-- We should store only the position information in the future.
|
| 1801 |
+
-- Recall that we will need to have a compact way of storing position information in the future anyway, if we
|
| 1802 |
+
-- want to support debugging information
|
| 1803 |
+
private def annotateIfRec (stx : Syntax) (e : Expr) : TermElabM Expr := do
|
| 1804 |
+
if (β read).saveRecAppSyntax then
|
| 1805 |
+
let resultFn := e.getAppFn
|
| 1806 |
+
if resultFn.isFVar then
|
| 1807 |
+
let localDecl β resultFn.fvarId!.getDecl
|
| 1808 |
+
if localDecl.isAuxDecl then
|
| 1809 |
+
return mkRecAppWithSyntax e stx
|
| 1810 |
+
return e
|
| 1811 |
+
|
| 1812 |
+
@[builtin_term_elab app] def elabApp : TermElab := fun stx expectedType? =>
|
| 1813 |
+
universeConstraintsCheckpoint do
|
| 1814 |
+
let (f, namedArgs, args, ellipsis) β expandApp stx
|
| 1815 |
+
annotateIfRec stx (β elabAppAux f namedArgs args (ellipsis := ellipsis) expectedType?)
|
| 1816 |
+
|
| 1817 |
+
private def elabAtom : TermElab := fun stx expectedType? => do
|
| 1818 |
+
annotateIfRec stx (β elabAppAux stx #[] #[] (ellipsis := false) expectedType?)
|
| 1819 |
+
|
| 1820 |
+
@[builtin_term_elab ident] def elabIdent : TermElab := elabAtom
|
| 1821 |
+
@[builtin_term_elab namedPattern] def elabNamedPattern : TermElab := elabAtom
|
| 1822 |
+
@[builtin_term_elab dotIdent] def elabDotIdent : TermElab := elabAtom
|
| 1823 |
+
@[builtin_term_elab explicitUniv] def elabExplicitUniv : TermElab := elabAtom
|
| 1824 |
+
@[builtin_term_elab pipeProj] def elabPipeProj : TermElab
|
| 1825 |
+
| `($e |>.%$tk$f $args*), expectedType? =>
|
| 1826 |
+
universeConstraintsCheckpoint do
|
| 1827 |
+
let (namedArgs, args, ellipsis) β expandArgs args
|
| 1828 |
+
let mut stx β `($e |>.%$tk$f)
|
| 1829 |
+
if let (some startPos, some stopPos) := (e.raw.getPos?, f.raw.getTailPos?) then
|
| 1830 |
+
stx := β¨stx.raw.setInfo <| .synthetic (canonical := true) startPos stopPosβ©
|
| 1831 |
+
elabAppAux stx namedArgs args (ellipsis := ellipsis) expectedType?
|
| 1832 |
+
| _, _ => throwUnsupportedSyntax
|
| 1833 |
+
|
| 1834 |
+
@[builtin_term_elab explicit] def elabExplicit : TermElab := fun stx expectedType? =>
|
| 1835 |
+
match stx with
|
| 1836 |
+
| `(@$_:ident) => elabAtom stx expectedType? -- Recall that `elabApp` also has support for `@`
|
| 1837 |
+
| `(@$_:ident.{$_us,*}) => elabAtom stx expectedType?
|
| 1838 |
+
| `(@$(_).$_:fieldIdx) => elabAtom stx expectedType?
|
| 1839 |
+
| `(@$(_).$_:ident) => elabAtom stx expectedType?
|
| 1840 |
+
| `(@($t)) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
|
| 1841 |
+
| `(@$t) => elabTerm t expectedType? (implicitLambda := false) -- `@` is being used just to disable implicit lambdas
|
| 1842 |
+
| _ => throwUnsupportedSyntax
|
| 1843 |
+
|
| 1844 |
+
@[builtin_term_elab choice] def elabChoice : TermElab := elabAtom
|
| 1845 |
+
@[builtin_term_elab proj] def elabProj : TermElab := elabAtom
|
| 1846 |
+
|
| 1847 |
+
builtin_initialize
|
| 1848 |
+
registerTraceClass `Elab.app
|
| 1849 |
+
registerTraceClass `Elab.app.args (inherited := true)
|
| 1850 |
+
registerTraceClass `Elab.app.propagateExpectedType (inherited := true)
|
| 1851 |
+
registerTraceClass `Elab.app.finalize (inherited := true)
|
| 1852 |
+
registerTraceClass `Elab.app.elab_as_elim (inherited := true)
|
| 1853 |
+
|
| 1854 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Arg.lean
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Term
|
| 8 |
+
|
| 9 |
+
namespace Lean.Elab.Term
|
| 10 |
+
|
| 11 |
+
/--
|
| 12 |
+
Auxiliary inductive datatype for combining unelaborated syntax
|
| 13 |
+
and already elaborated expressions. It is used to elaborate applications.
|
| 14 |
+
-/
|
| 15 |
+
inductive Arg where
|
| 16 |
+
| stx (val : Syntax)
|
| 17 |
+
| expr (val : Expr)
|
| 18 |
+
deriving Inhabited
|
| 19 |
+
|
| 20 |
+
/-- Named arguments created using the notation `(x := val)`. -/
|
| 21 |
+
structure NamedArg where
|
| 22 |
+
ref : Syntax := Syntax.missing
|
| 23 |
+
name : Name
|
| 24 |
+
val : Arg
|
| 25 |
+
/-- If `true`, then make all parameters that depend on this one become implicit.
|
| 26 |
+
This is used for projection notation, since structure parameters might be explicit for classes. -/
|
| 27 |
+
suppressDeps : Bool := false
|
| 28 |
+
deriving Inhabited
|
| 29 |
+
|
| 30 |
+
instance : ToMessageData Arg where
|
| 31 |
+
toMessageData
|
| 32 |
+
| .stx stx => toMessageData stx
|
| 33 |
+
| .expr e => toMessageData e
|
| 34 |
+
|
| 35 |
+
/--
|
| 36 |
+
Add a new named argument to `namedArgs`, and throw an error if it already contains a named argument
|
| 37 |
+
with the same name. -/
|
| 38 |
+
def addNamedArg (namedArgs : Array NamedArg) (namedArg : NamedArg) : MetaM (Array NamedArg) := do
|
| 39 |
+
if namedArgs.any (namedArg.name == Β·.name) then
|
| 40 |
+
throwError "argument '{namedArg.name}' was already set"
|
| 41 |
+
return namedArgs.push namedArg
|
| 42 |
+
|
| 43 |
+
partial def expandArgs (args : Array Syntax) : MetaM (Array NamedArg Γ Array Arg Γ Bool) := do
|
| 44 |
+
let (args, ellipsis) :=
|
| 45 |
+
if args.isEmpty then
|
| 46 |
+
(args, false)
|
| 47 |
+
else if args.back!.isOfKind ``Lean.Parser.Term.ellipsis then
|
| 48 |
+
(args.pop, true)
|
| 49 |
+
else
|
| 50 |
+
(args, false)
|
| 51 |
+
let (namedArgs, args) β args.foldlM (init := (#[], #[])) fun (namedArgs, args) stx => do
|
| 52 |
+
if stx.getKind == ``Lean.Parser.Term.namedArgument then
|
| 53 |
+
-- trailing_tparser try ("(" >> ident >> " := ") >> termParser >> ")"
|
| 54 |
+
let name := stx[1].getId.eraseMacroScopes
|
| 55 |
+
let val := stx[3]
|
| 56 |
+
let namedArgs β addNamedArg namedArgs { ref := stx, name := name, val := Arg.stx val }
|
| 57 |
+
return (namedArgs, args)
|
| 58 |
+
else if stx.getKind == ``Lean.Parser.Term.ellipsis then
|
| 59 |
+
throwErrorAt stx "unexpected '..'"
|
| 60 |
+
else
|
| 61 |
+
return (namedArgs, args.push $ Arg.stx stx)
|
| 62 |
+
return (namedArgs, args, ellipsis)
|
| 63 |
+
|
| 64 |
+
def expandApp (stx : Syntax) : MetaM (Syntax Γ Array NamedArg Γ Array Arg Γ Bool) := do
|
| 65 |
+
let (namedArgs, args, ellipsis) β expandArgs stx[1].getArgs
|
| 66 |
+
return (stx[0], namedArgs, args, ellipsis)
|
| 67 |
+
|
| 68 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Attributes.lean
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Util
|
| 8 |
+
namespace Lean.Elab
|
| 9 |
+
|
| 10 |
+
structure Attribute where
|
| 11 |
+
kind : AttributeKind := AttributeKind.global
|
| 12 |
+
name : Name
|
| 13 |
+
stx : Syntax := Syntax.missing
|
| 14 |
+
deriving Inhabited
|
| 15 |
+
|
| 16 |
+
instance : ToFormat Attribute where
|
| 17 |
+
format attr :=
|
| 18 |
+
let kindStr := match attr.kind with
|
| 19 |
+
| AttributeKind.global => ""
|
| 20 |
+
| AttributeKind.local => "local "
|
| 21 |
+
| AttributeKind.scoped => "scoped "
|
| 22 |
+
Format.bracket "@[" f!"{kindStr}{attr.name}{toString attr.stx}" "]"
|
| 23 |
+
|
| 24 |
+
/--
|
| 25 |
+
```
|
| 26 |
+
attrKind := leading_parser optional (Β«scopedΒ» <|> Β«localΒ»)
|
| 27 |
+
```
|
| 28 |
+
-/
|
| 29 |
+
def toAttributeKind (attrKindStx : Syntax) : MacroM AttributeKind := do
|
| 30 |
+
if attrKindStx[0].isNone then
|
| 31 |
+
return AttributeKind.global
|
| 32 |
+
else if attrKindStx[0][0].getKind == ``Lean.Parser.Term.scoped then
|
| 33 |
+
if (β Macro.getCurrNamespace).isAnonymous then
|
| 34 |
+
throw <| Macro.Exception.error (β getRef) "scoped attributes must be used inside namespaces"
|
| 35 |
+
return AttributeKind.scoped
|
| 36 |
+
else
|
| 37 |
+
return AttributeKind.local
|
| 38 |
+
|
| 39 |
+
def mkAttrKindGlobal : Syntax :=
|
| 40 |
+
mkNode ``Lean.Parser.Term.attrKind #[mkNullNode]
|
| 41 |
+
|
| 42 |
+
def elabAttr [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLiftT IO m] (attrInstance : Syntax) : m Attribute := do
|
| 43 |
+
/- attrInstance := ppGroup $ leading_parser attrKind >> attrParser -/
|
| 44 |
+
let attrKind β liftMacroM <| toAttributeKind attrInstance[0]
|
| 45 |
+
let attr := attrInstance[1]
|
| 46 |
+
let attr β liftMacroM <| expandMacros attr
|
| 47 |
+
let attrName β if attr.getKind == ``Parser.Attr.simple then
|
| 48 |
+
pure attr[0].getId.eraseMacroScopes
|
| 49 |
+
else match attr.getKind with
|
| 50 |
+
| .str _ s => pure <| Name.mkSimple s
|
| 51 |
+
| _ => throwErrorAt attr "unknown attribute"
|
| 52 |
+
let .ok _impl := getAttributeImpl (β getEnv) attrName
|
| 53 |
+
| throwError "unknown attribute [{attrName}]"
|
| 54 |
+
/- The `AttrM` does not have sufficient information for expanding macros in `args`.
|
| 55 |
+
So, we expand them before here before we invoke the attributer handlers implemented using `AttrM`. -/
|
| 56 |
+
return { kind := attrKind, name := attrName, stx := attr }
|
| 57 |
+
|
| 58 |
+
def elabAttrs [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadLiftT IO m] (attrInstances : Array Syntax) : m (Array Attribute) := do
|
| 59 |
+
let mut attrs := #[]
|
| 60 |
+
for attr in attrInstances do
|
| 61 |
+
try
|
| 62 |
+
attrs := attrs.push (β withRef attr do elabAttr attr)
|
| 63 |
+
catch ex =>
|
| 64 |
+
logException ex
|
| 65 |
+
return attrs
|
| 66 |
+
|
| 67 |
+
-- leading_parser "@[" >> sepBy1 attrInstance ", " >> "]"
|
| 68 |
+
def elabDeclAttrs [Monad m] [MonadEnv m] [MonadResolveName m] [MonadError m] [MonadMacroAdapter m] [MonadRecDepth m] [MonadTrace m] [MonadOptions m] [AddMessageContext m] [MonadLog m] [MonadLiftT IO m] (stx : Syntax) : m (Array Attribute) :=
|
| 69 |
+
elabAttrs stx[1].getSepArgs
|
| 70 |
+
|
| 71 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/AutoBound.lean
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.Options
|
| 8 |
+
|
| 9 |
+
/-! # Basic support for auto bound implicit local names -/
|
| 10 |
+
|
| 11 |
+
namespace Lean.Elab
|
| 12 |
+
|
| 13 |
+
register_builtin_option autoImplicit : Bool := {
|
| 14 |
+
defValue := true
|
| 15 |
+
descr := "Unbound local variables in declaration headers become implicit arguments. In \"relaxed\" mode (default), any atomic identifier is eligible, otherwise only single character followed by numeric digits are eligible. For example, `def f (x : Vector Ξ± n) : Vector Ξ± n :=` automatically introduces the implicit variables {Ξ± n}."
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
register_builtin_option relaxedAutoImplicit : Bool := {
|
| 19 |
+
defValue := true
|
| 20 |
+
descr := "When \"relaxed\" mode is enabled, any atomic nonempty identifier is eligible for auto bound implicit locals (see option `autoImplicit`)."
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
private def isValidAutoBoundSuffix (s : String) : Bool :=
|
| 25 |
+
s.toSubstring.drop 1 |>.all fun c => c.isDigit || isSubScriptAlnum c || c == '_' || c == '\''
|
| 26 |
+
|
| 27 |
+
/-!
|
| 28 |
+
Remark: Issue #255 exposed a nasty interaction between macro scopes and auto-bound-implicit names.
|
| 29 |
+
```
|
| 30 |
+
local notation "A" => id x
|
| 31 |
+
theorem test : A = A := sorry
|
| 32 |
+
```
|
| 33 |
+
We used to use `n.eraseMacroScopes` at `isValidAutoBoundImplicitName` and `isValidAutoBoundLevelName`.
|
| 34 |
+
Thus, in the example above, when `A` is expanded, a `x` with a fresh macro scope is created.
|
| 35 |
+
`x`+macros-scope is not in scope and is a valid auto-bound implicit name after macro scopes are erased.
|
| 36 |
+
So, an auto-bound exception would be thrown, and `x`+macro-scope would be added as a new implicit.
|
| 37 |
+
When, we try again, a `x` with a new macro scope is created and this process keeps repeating.
|
| 38 |
+
Therefore, we do consider identifier with macro scopes anymore.
|
| 39 |
+
-/
|
| 40 |
+
|
| 41 |
+
def isValidAutoBoundImplicitName (n : Name) (relaxed : Bool) : Bool :=
|
| 42 |
+
match n with
|
| 43 |
+
| .str .anonymous s => s.length > 0 && (relaxed || isValidAutoBoundSuffix s)
|
| 44 |
+
| _ => false
|
| 45 |
+
|
| 46 |
+
def isValidAutoBoundLevelName (n : Name) (relaxed : Bool) : Bool :=
|
| 47 |
+
match n with
|
| 48 |
+
| .str .anonymous s => s.length > 0 && (relaxed || (s.front.isLower && isValidAutoBoundSuffix s))
|
| 49 |
+
| _ => false
|
| 50 |
+
|
| 51 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/AuxDef.lean
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Gabriel Ebner
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Command
|
| 8 |
+
|
| 9 |
+
namespace Lean.Elab.Command
|
| 10 |
+
open Lean.Parser.Command
|
| 11 |
+
open Lean.Parser.Term
|
| 12 |
+
|
| 13 |
+
/--
|
| 14 |
+
Declares an auxiliary definition with an automatically generated name.
|
| 15 |
+
For example, `aux_def foo : Nat := 42` creates a definition
|
| 16 |
+
with an internal, unused name based on the suggestion `foo`.
|
| 17 |
+
-/
|
| 18 |
+
scoped syntax (name := aux_def) docComment ? attributes ? "aux_def" ident+ ":" term ":=" term : command
|
| 19 |
+
|
| 20 |
+
@[builtin_command_elab Β«aux_defΒ»]
|
| 21 |
+
def elabAuxDef : CommandElab
|
| 22 |
+
| `($[$doc?:docComment]? $[$attrs?:attributes]? aux_def $suggestion* : $ty := $body) => do
|
| 23 |
+
let id := suggestion.map (Β·.getId.eraseMacroScopes) |>.foldl (Β· ++ Β·) Name.anonymous
|
| 24 |
+
let id := `_aux ++ (β getMainModule) ++ `_ ++ id
|
| 25 |
+
let id := String.intercalate "_" <| id.components.map (Β·.toString (escape := false))
|
| 26 |
+
let ns β getCurrNamespace
|
| 27 |
+
-- We use a new generator here because we want more control over the name; the default would
|
| 28 |
+
-- create a private name that then breaks the macro below. We assume that `aux_def` is not used
|
| 29 |
+
-- with the same arguments in parallel contexts.
|
| 30 |
+
let env := (β getEnv).setExporting true
|
| 31 |
+
let (id, _) := { namePrefix := ns : DeclNameGenerator }.mkUniqueName env (Β«infixΒ» := Name.mkSimple id)
|
| 32 |
+
let id := id.replacePrefix ns Name.anonymous -- TODO: replace with def _root_.id
|
| 33 |
+
elabCommand <|
|
| 34 |
+
β `($[$doc?:docComment]? $[$attrs?:attributes]?
|
| 35 |
+
meta def $(mkIdentFrom (mkNullNode suggestion) id (canonical := true)):ident : $ty := $body)
|
| 36 |
+
| _ => throwUnsupportedSyntax
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BinderPredicates.lean
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Gabriel Ebner
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.BinderPredicates
|
| 8 |
+
import Lean.Parser.Syntax
|
| 9 |
+
import Lean.Elab.MacroArgUtil
|
| 10 |
+
import Lean.Linter.MissingDocs
|
| 11 |
+
|
| 12 |
+
namespace Lean.Elab.Command
|
| 13 |
+
|
| 14 |
+
@[builtin_command_elab binderPredicate] def elabBinderPred : CommandElab := fun stx => do
|
| 15 |
+
match stx with
|
| 16 |
+
| `($[$doc?:docComment]? $[@[$attrs?,*]]? $attrKind:attrKind binder_predicate%$tk
|
| 17 |
+
$[(name := $name?)]? $[(priority := $prio?)]? $x $args:macroArg* => $rhs) => do
|
| 18 |
+
let prio β liftMacroM do evalOptPrio prio?
|
| 19 |
+
let (stxParts, patArgs) := (β args.mapM expandMacroArg).unzip
|
| 20 |
+
let name β match name? with
|
| 21 |
+
| some name => pure name.getId
|
| 22 |
+
| none => liftMacroM do mkNameFromParserSyntax `binderTerm (mkNullNode stxParts)
|
| 23 |
+
let nameTk := name?.getD (mkIdentFrom tk name)
|
| 24 |
+
/- The command `syntax [<kind>] ...` adds the current namespace to the syntax node kind.
|
| 25 |
+
So, we must include current namespace when we create a pattern for the following
|
| 26 |
+
`macro_rules` commands. -/
|
| 27 |
+
let pat : TSyntax `binderPred := β¨(mkNode ((β getCurrNamespace) ++ name) patArgs).1β©
|
| 28 |
+
elabCommand <|<-
|
| 29 |
+
`($[$doc?:docComment]? $[@[$attrs?,*]]? $attrKind:attrKind syntax%$tk
|
| 30 |
+
(name := $nameTk) (priority := $(quote prio)) $[$stxParts]* : binderPred
|
| 31 |
+
$[$doc?:docComment]? macro_rules%$tk
|
| 32 |
+
| `(satisfies_binder_pred% $$($x):term $pat:binderPred) => $rhs)
|
| 33 |
+
| _ => throwUnsupportedSyntax
|
| 34 |
+
|
| 35 |
+
open Linter.MissingDocs Parser Term in
|
| 36 |
+
/-- Missing docs handler for `binder_predicate` -/
|
| 37 |
+
@[builtin_missing_docs_handler Lean.Parser.Command.binderPredicate]
|
| 38 |
+
def checkBinderPredicate : SimpleHandler := fun stx => do
|
| 39 |
+
if stx[0].isNone && stx[2][0][0].getKind != ``Β«localΒ» then
|
| 40 |
+
if stx[4].isNone then lint stx[3] "binder predicate"
|
| 41 |
+
else lintNamed stx[4][0][3] "binder predicate"
|
| 42 |
+
|
| 43 |
+
end Lean.Elab.Command
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Binders.lean
ADDED
|
@@ -0,0 +1,957 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Quotation.Precheck
|
| 8 |
+
import Lean.Elab.Term
|
| 9 |
+
import Lean.Elab.BindersUtil
|
| 10 |
+
import Lean.Elab.SyntheticMVars
|
| 11 |
+
import Lean.Elab.PreDefinition.TerminationHint
|
| 12 |
+
import Lean.Elab.Match
|
| 13 |
+
import Lean.Compiler.MetaAttr
|
| 14 |
+
|
| 15 |
+
namespace Lean.Elab.Term
|
| 16 |
+
open Meta
|
| 17 |
+
open Lean.Parser.Term
|
| 18 |
+
open TSyntax.Compat
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
Given syntax of the forms
|
| 22 |
+
a) (`:` term)?
|
| 23 |
+
b) `:` term
|
| 24 |
+
return `term` if it is present, or a hole if not. -/
|
| 25 |
+
private def expandBinderType (ref : Syntax) (stx : Syntax) : Syntax :=
|
| 26 |
+
if stx.getNumArgs == 0 then
|
| 27 |
+
mkHole ref
|
| 28 |
+
else
|
| 29 |
+
stx[1]
|
| 30 |
+
|
| 31 |
+
/-- Given syntax of the form `ident <|> hole`, return `ident`. If `hole`, then we create a new anonymous name. -/
|
| 32 |
+
private def expandBinderIdent (stx : Syntax) : TermElabM Syntax :=
|
| 33 |
+
match stx with
|
| 34 |
+
| `(_) => mkFreshIdent stx (canonical := true)
|
| 35 |
+
| _ => pure stx
|
| 36 |
+
|
| 37 |
+
/-- Given syntax of the form `(ident >> " : ")?`, return `ident`, or a new instance name. -/
|
| 38 |
+
private def expandOptIdent (stx : Syntax) : TermElabM Syntax := do
|
| 39 |
+
if stx.isNone then
|
| 40 |
+
let id β withFreshMacroScope <| MonadQuotation.addMacroScope `inst
|
| 41 |
+
return mkIdentFrom stx id
|
| 42 |
+
else
|
| 43 |
+
return stx[0]
|
| 44 |
+
|
| 45 |
+
/-- Auxiliary datatype for elaborating binders. -/
|
| 46 |
+
structure BinderView where
|
| 47 |
+
/--
|
| 48 |
+
Position information provider for the Info Tree.
|
| 49 |
+
We currently do not track binder "macro expansion" steps in the info tree.
|
| 50 |
+
For example, suppose we expand a `_` into a fresh identifier. The fresh identifier
|
| 51 |
+
has synthetic position since it was not written by the user, and we would not get
|
| 52 |
+
hover information for the `_` because we also don't have this macro expansion step
|
| 53 |
+
stored in the info tree. Thus, we store the original `Syntax` in `ref`, and use
|
| 54 |
+
it when storing the binder information in the info tree.
|
| 55 |
+
|
| 56 |
+
Potential better solution: add a binder syntax category, an extensible `elabBinder`
|
| 57 |
+
(like we have `elabTerm`), and perform all macro expansion steps at `elabBinder` and
|
| 58 |
+
record them in the infro tree.
|
| 59 |
+
-/
|
| 60 |
+
ref : Syntax
|
| 61 |
+
id : Syntax
|
| 62 |
+
type : Syntax
|
| 63 |
+
bi : BinderInfo
|
| 64 |
+
|
| 65 |
+
/--
|
| 66 |
+
Determines the local declaration kind depending on the variable name.
|
| 67 |
+
|
| 68 |
+
The `__x` in `let __x := 42; body` gets kind `.implDetail`.
|
| 69 |
+
-/
|
| 70 |
+
def kindOfBinderName (binderName : Name) : LocalDeclKind :=
|
| 71 |
+
if binderName.isImplementationDetail then
|
| 72 |
+
.implDetail
|
| 73 |
+
else
|
| 74 |
+
.default
|
| 75 |
+
|
| 76 |
+
partial def quoteAutoTactic : Syntax β CoreM Expr
|
| 77 |
+
| .ident _ _ val preresolved =>
|
| 78 |
+
return mkApp4 (.const ``Syntax.ident [])
|
| 79 |
+
(.const ``SourceInfo.none [])
|
| 80 |
+
(.app (.const ``String.toSubstring []) (mkStrLit (toString val)))
|
| 81 |
+
(toExpr val)
|
| 82 |
+
(toExpr preresolved)
|
| 83 |
+
| stx@(.node _ k args) => do
|
| 84 |
+
if stx.isAntiquot then
|
| 85 |
+
throwErrorAt stx "invalid auto tactic, antiquotation is not allowed"
|
| 86 |
+
else
|
| 87 |
+
let ty := .const ``Syntax []
|
| 88 |
+
let mut quotedArgs := mkApp (.const ``Array.empty [.zero]) ty
|
| 89 |
+
for arg in args do
|
| 90 |
+
if k == nullKind && (arg.isAntiquotSuffixSplice || arg.isAntiquotSplice) then
|
| 91 |
+
throwErrorAt arg "invalid auto tactic, antiquotation is not allowed"
|
| 92 |
+
else
|
| 93 |
+
let quotedArg β quoteAutoTactic arg
|
| 94 |
+
quotedArgs := mkApp3 (.const ``Array.push [.zero]) ty quotedArgs quotedArg
|
| 95 |
+
return mkApp3 (.const ``Syntax.node []) (.const ``SourceInfo.none []) (toExpr k) quotedArgs
|
| 96 |
+
| .atom _ val => return .app (.const ``mkAtom []) (toExpr val)
|
| 97 |
+
| .missing => throwError "invalid auto tactic, tactic is missing"
|
| 98 |
+
|
| 99 |
+
/--
|
| 100 |
+
Adds a declaration whose value is a Syntax expression representing `tactic`.
|
| 101 |
+
If `name?` is provided, it is used for the declaration name, and otherwise a fresh name is generated.
|
| 102 |
+
Returns the declaration name.
|
| 103 |
+
-/
|
| 104 |
+
def declareTacticSyntax (tactic : Syntax) (name? : Option Name := none) : TermElabM Name :=
|
| 105 |
+
withFreshMacroScope do
|
| 106 |
+
let name β name?.getDM do MonadQuotation.addMacroScope ((β getEnv).asyncPrefix?.getD .anonymous ++ `_auto)
|
| 107 |
+
let type := Lean.mkConst `Lean.Syntax
|
| 108 |
+
let value β quoteAutoTactic tactic
|
| 109 |
+
trace[Elab.autoParam] value
|
| 110 |
+
let decl := Declaration.defnDecl { name, levelParams := [], type, value, hints := .opaque,
|
| 111 |
+
safety := DefinitionSafety.safe }
|
| 112 |
+
addDecl decl
|
| 113 |
+
modifyEnv (addMeta Β· name)
|
| 114 |
+
compileDecl decl
|
| 115 |
+
return name
|
| 116 |
+
|
| 117 |
+
/--
|
| 118 |
+
Expand `optional (binderTactic <|> binderDefault)`
|
| 119 |
+
```
|
| 120 |
+
def binderTactic := leading_parser " := " >> " by " >> tacticParser
|
| 121 |
+
def binderDefault := leading_parser " := " >> termParser
|
| 122 |
+
```
|
| 123 |
+
-/
|
| 124 |
+
private def expandBinderModifier (type : Syntax) (optBinderModifier : Syntax) : TermElabM Syntax := do
|
| 125 |
+
if optBinderModifier.isNone then
|
| 126 |
+
return type
|
| 127 |
+
else
|
| 128 |
+
let modifier := optBinderModifier[0]
|
| 129 |
+
let kind := modifier.getKind
|
| 130 |
+
if kind == `Lean.Parser.Term.binderDefault then
|
| 131 |
+
let defaultVal := modifier[1]
|
| 132 |
+
`(optParam $type $defaultVal)
|
| 133 |
+
else if kind == `Lean.Parser.Term.binderTactic then
|
| 134 |
+
let tac := modifier[2]
|
| 135 |
+
let name β declareTacticSyntax tac
|
| 136 |
+
`(autoParam $type $(mkIdentFrom tac name))
|
| 137 |
+
else
|
| 138 |
+
throwUnsupportedSyntax
|
| 139 |
+
|
| 140 |
+
private def getBinderIds (ids : Syntax) : TermElabM (Array Syntax) :=
|
| 141 |
+
ids.getArgs.mapM fun id =>
|
| 142 |
+
let k := id.getKind
|
| 143 |
+
if k == identKind || k == `Lean.Parser.Term.hole then
|
| 144 |
+
return id
|
| 145 |
+
else
|
| 146 |
+
throwErrorAt id "identifier or `_` expected"
|
| 147 |
+
|
| 148 |
+
/--
|
| 149 |
+
Convert `stx` into an array of `BinderView`s.
|
| 150 |
+
`stx` must be an identifier, `_`, `explicitBinder`, `implicitBinder`, `strictImplicitBinder`, or `instBinder`.
|
| 151 |
+
-/
|
| 152 |
+
private def toBinderViews (stx : Syntax) : TermElabM (Array BinderView) := do
|
| 153 |
+
let k := stx.getKind
|
| 154 |
+
if stx.isIdent || k == ``hole then
|
| 155 |
+
-- binderIdent
|
| 156 |
+
return #[{ ref := stx, id := (β expandBinderIdent stx), type := mkHole stx, bi := .default }]
|
| 157 |
+
else if k == ``Lean.Parser.Term.explicitBinder then
|
| 158 |
+
-- `(` binderIdent+ binderType (binderDefault <|> binderTactic)? `)`
|
| 159 |
+
let ids β getBinderIds stx[1]
|
| 160 |
+
let type := stx[2]
|
| 161 |
+
let optModifier := stx[3]
|
| 162 |
+
ids.mapM fun id => do pure { ref := id, id := (β expandBinderIdent id), type := (β expandBinderModifier (expandBinderType id type) optModifier), bi := .default }
|
| 163 |
+
else if k == ``Lean.Parser.Term.implicitBinder then
|
| 164 |
+
-- `{` binderIdent+ binderType `}`
|
| 165 |
+
let ids β getBinderIds stx[1]
|
| 166 |
+
let type := stx[2]
|
| 167 |
+
ids.mapM fun id => do pure { ref := id, id := (β expandBinderIdent id), type := expandBinderType id type, bi := .implicit }
|
| 168 |
+
else if k == ``Lean.Parser.Term.strictImplicitBinder then
|
| 169 |
+
-- `β¦` binderIdent+ binderType `β¦`
|
| 170 |
+
let ids β getBinderIds stx[1]
|
| 171 |
+
let type := stx[2]
|
| 172 |
+
ids.mapM fun id => do pure { ref := id, id := (β expandBinderIdent id), type := expandBinderType id type, bi := .strictImplicit }
|
| 173 |
+
else if k == ``Lean.Parser.Term.instBinder then
|
| 174 |
+
-- `[` optIdent type `]`
|
| 175 |
+
let id β expandOptIdent stx[1]
|
| 176 |
+
let type := stx[2]
|
| 177 |
+
return #[ { ref := id, id := id, type := type, bi := .instImplicit } ]
|
| 178 |
+
else
|
| 179 |
+
throwUnsupportedSyntax
|
| 180 |
+
|
| 181 |
+
private def registerFailedToInferBinderTypeInfo (type : Expr) (ref : Syntax) : TermElabM Unit := do
|
| 182 |
+
registerCustomErrorIfMVar type ref "failed to infer binder type"
|
| 183 |
+
registerLevelMVarErrorExprInfo type ref m!"failed to infer universe levels in binder type"
|
| 184 |
+
|
| 185 |
+
def addLocalVarInfo (stx : Syntax) (fvar : Expr) : TermElabM Unit :=
|
| 186 |
+
addTermInfo' (isBinder := true) stx fvar
|
| 187 |
+
|
| 188 |
+
private def ensureAtomicBinderName (binderView : BinderView) : TermElabM Unit :=
|
| 189 |
+
let n := binderView.id.getId.eraseMacroScopes
|
| 190 |
+
unless n.isAtomic do
|
| 191 |
+
throwErrorAt binderView.id "invalid binder name '{n}', it must be atomic"
|
| 192 |
+
|
| 193 |
+
register_builtin_option checkBinderAnnotations : Bool := {
|
| 194 |
+
defValue := true
|
| 195 |
+
descr := "check whether type is a class instance whenever the binder annotation `[...]` is used"
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
/-- Throw an error if `type` is not a valid local instance. -/
|
| 199 |
+
private partial def checkLocalInstanceParameters (type : Expr) : TermElabM Unit := do
|
| 200 |
+
let .forallE n d b bi β whnf type | return ()
|
| 201 |
+
-- We allow instance arguments so that local instances of the form
|
| 202 |
+
-- `variable [β (a : Ξ±) [P a], Q a]`
|
| 203 |
+
-- are accepted, per https://github.com/leanprover/lean4/issues/2311
|
| 204 |
+
if bi != .instImplicit && !b.hasLooseBVar 0 then
|
| 205 |
+
throwError "invalid parametric local instance, parameter with type{indentExpr d}\ndoes not have forward dependencies, type class resolution cannot use this kind of local instance because it will not be able to infer a value for this parameter."
|
| 206 |
+
withLocalDecl n bi d fun x => checkLocalInstanceParameters (b.instantiate1 x)
|
| 207 |
+
|
| 208 |
+
private partial def elabBinderViews (binderViews : Array BinderView) (fvars : Array (Syntax Γ Expr)) (k : Array (Syntax Γ Expr) β TermElabM Ξ±)
|
| 209 |
+
: TermElabM Ξ± :=
|
| 210 |
+
let rec loop (i : Nat) (fvars : Array (Syntax Γ Expr)) : TermElabM Ξ± := do
|
| 211 |
+
if h : i < binderViews.size then
|
| 212 |
+
let binderView := binderViews[i]
|
| 213 |
+
ensureAtomicBinderName binderView
|
| 214 |
+
let type β elabType binderView.type
|
| 215 |
+
registerFailedToInferBinderTypeInfo type binderView.type
|
| 216 |
+
if binderView.bi.isInstImplicit && checkBinderAnnotations.get (β getOptions) then
|
| 217 |
+
unless (β isClass? type).isSome do
|
| 218 |
+
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")
|
| 219 |
+
withRef binderView.type <| checkLocalInstanceParameters type
|
| 220 |
+
let id := binderView.id.getId
|
| 221 |
+
let kind := kindOfBinderName id
|
| 222 |
+
withLocalDecl id binderView.bi type (kind := kind) fun fvar => do
|
| 223 |
+
addLocalVarInfo binderView.ref fvar
|
| 224 |
+
loop (i+1) (fvars.push (binderView.id, fvar))
|
| 225 |
+
else
|
| 226 |
+
k fvars
|
| 227 |
+
loop 0 fvars
|
| 228 |
+
|
| 229 |
+
private partial def elabBindersAux (binders : Array Syntax) (k : Array (Syntax Γ Expr) β TermElabM Ξ±) : TermElabM Ξ± :=
|
| 230 |
+
let rec loop (i : Nat) (fvars : Array (Syntax Γ Expr)) : TermElabM Ξ± := do
|
| 231 |
+
if h : i < binders.size then
|
| 232 |
+
let binderViews β toBinderViews binders[i]
|
| 233 |
+
elabBinderViews binderViews fvars <| loop (i+1)
|
| 234 |
+
else
|
| 235 |
+
k fvars
|
| 236 |
+
loop 0 #[]
|
| 237 |
+
|
| 238 |
+
/--
|
| 239 |
+
Like `elabBinders`, but also pass syntax node per binder.
|
| 240 |
+
`elabBinders(Ex)` automatically adds binder info nodes for the produced fvars, but storing the syntax nodes
|
| 241 |
+
might be necessary when later adding the same binders back to the local context so that info nodes can
|
| 242 |
+
manually be added for the new fvars; see `MutualDef` for an example. -/
|
| 243 |
+
def elabBindersEx (binders : Array Syntax) (k : Array (Syntax Γ Expr) β TermElabM Ξ±) : TermElabM Ξ± :=
|
| 244 |
+
universeConstraintsCheckpoint do
|
| 245 |
+
if binders.isEmpty then
|
| 246 |
+
k #[]
|
| 247 |
+
else
|
| 248 |
+
elabBindersAux binders k
|
| 249 |
+
|
| 250 |
+
/--
|
| 251 |
+
Elaborate the given binders (i.e., `Syntax` objects for `bracketedBinder`),
|
| 252 |
+
update the local context, set of local instances, reset instance cache (if needed), and then
|
| 253 |
+
execute `k` with the updated context.
|
| 254 |
+
The local context will only be included inside `k`.
|
| 255 |
+
|
| 256 |
+
For example, suppose you have binders `[(a : Ξ±), (b : Ξ² a)]`, then the elaborator will
|
| 257 |
+
create two new free variables `a` and `b`, push these to the context and pass to `k #[a,b]`.
|
| 258 |
+
-/
|
| 259 |
+
def elabBinders (binders : Array Syntax) (k : Array Expr β TermElabM Ξ±) : TermElabM Ξ± :=
|
| 260 |
+
elabBindersEx binders (fun fvars => k (fvars.map (Β·.2)))
|
| 261 |
+
|
| 262 |
+
/-- Same as `elabBinder` with a single binder.-/
|
| 263 |
+
def elabBinder (binder : Syntax) (x : Expr β TermElabM Ξ±) : TermElabM Ξ± :=
|
| 264 |
+
elabBinders #[binder] fun fvars => x fvars[0]!
|
| 265 |
+
|
| 266 |
+
/-- If `binder` is a `_` or an identifier, return a `bracketedBinder` using `type` otherwise throw an exception. -/
|
| 267 |
+
def expandSimpleBinderWithType (type : Term) (binder : Syntax) : MacroM Syntax :=
|
| 268 |
+
if binder.isOfKind ``hole || binder.isIdent then
|
| 269 |
+
`(bracketedBinderF| ($binder : $type))
|
| 270 |
+
else
|
| 271 |
+
Macro.throwErrorAt type "unexpected type ascription"
|
| 272 |
+
|
| 273 |
+
@[builtin_macro Lean.Parser.Term.forall] def expandForall : Macro
|
| 274 |
+
| `(forall $binders* : $ty, $term) => do
|
| 275 |
+
let binders β binders.mapM (expandSimpleBinderWithType ty)
|
| 276 |
+
`(forall $binders*, $term)
|
| 277 |
+
| _ => Macro.throwUnsupported
|
| 278 |
+
|
| 279 |
+
@[builtin_term_elab Β«forallΒ»] def elabForall : TermElab := fun stx _ =>
|
| 280 |
+
match stx with
|
| 281 |
+
| `(forall $binders*, $term) =>
|
| 282 |
+
elabBinders binders fun xs => do
|
| 283 |
+
let e β elabType term
|
| 284 |
+
mkForallFVars xs e
|
| 285 |
+
| _ => throwUnsupportedSyntax
|
| 286 |
+
|
| 287 |
+
open Lean.Elab.Term.Quotation in
|
| 288 |
+
@[builtin_quot_precheck Lean.Parser.Term.arrow] def precheckArrow : Precheck
|
| 289 |
+
| `($dom:term -> $rng) => do
|
| 290 |
+
precheck dom
|
| 291 |
+
precheck rng
|
| 292 |
+
| _ => throwUnsupportedSyntax
|
| 293 |
+
|
| 294 |
+
@[builtin_term_elab arrow] def elabArrow : TermElab := fun stx _ =>
|
| 295 |
+
match stx with
|
| 296 |
+
| `($dom:term -> $rng) => do
|
| 297 |
+
-- elaborate independently from each other
|
| 298 |
+
let dom β elabType dom
|
| 299 |
+
let rng β elabType rng
|
| 300 |
+
return mkForall (β MonadQuotation.addMacroScope `a) BinderInfo.default dom rng
|
| 301 |
+
| _ => throwUnsupportedSyntax
|
| 302 |
+
|
| 303 |
+
/--
|
| 304 |
+
The dependent arrow. `(x : Ξ±) β Ξ²` is equivalent to `β x : Ξ±, Ξ²`, but we usually
|
| 305 |
+
reserve the latter for propositions. Also written as `Ξ x : Ξ±, Ξ²` (the "Pi-type")
|
| 306 |
+
in the literature. -/
|
| 307 |
+
@[builtin_term_elab depArrow] def elabDepArrow : TermElab := fun stx _ =>
|
| 308 |
+
-- bracketedBinder `->` term
|
| 309 |
+
let binder := stx[0]
|
| 310 |
+
let term := stx[2]
|
| 311 |
+
elabBinders #[binder] fun xs => do
|
| 312 |
+
mkForallFVars xs (β elabType term)
|
| 313 |
+
|
| 314 |
+
/--
|
| 315 |
+
Auxiliary functions for converting `id_1 ... id_n` application into `#[id_1, ..., id_m]`
|
| 316 |
+
It is used at `expandFunBinders`. -/
|
| 317 |
+
private partial def getFunBinderIds? (stx : Syntax) : OptionT MacroM (Array Syntax) :=
|
| 318 |
+
let convertElem (stx : Term) : OptionT MacroM Syntax :=
|
| 319 |
+
match stx with
|
| 320 |
+
| `(_) =>
|
| 321 |
+
/-
|
| 322 |
+
We used to use `mkFreshIdent` here,
|
| 323 |
+
but it prevented us from obtaining hover info for `_` because the
|
| 324 |
+
fresh identifier would have a synthetic position, and synthetic positions
|
| 325 |
+
are ignored by the LSP server.
|
| 326 |
+
See comment at `BinderView.ref` for additional details.
|
| 327 |
+
-/
|
| 328 |
+
return stx
|
| 329 |
+
| `($_:ident) => return stx
|
| 330 |
+
| _ => failure
|
| 331 |
+
match stx with
|
| 332 |
+
| `($f $args*) => do
|
| 333 |
+
let mut acc := #[].push (β convertElem f)
|
| 334 |
+
for arg in args do
|
| 335 |
+
acc := acc.push (β convertElem arg)
|
| 336 |
+
return acc
|
| 337 |
+
| _ =>
|
| 338 |
+
return #[].push (β convertElem stx)
|
| 339 |
+
|
| 340 |
+
/--
|
| 341 |
+
Auxiliary function for expanding `fun` notation binders. Recall that `fun` parser is defined as
|
| 342 |
+
```
|
| 343 |
+
def funBinder : Parser := implicitBinder <|> instBinder <|> termParser maxPrec
|
| 344 |
+
leading_parser unicodeSymbol "Ξ»" "fun" >> many1 funBinder >> "=>" >> termParser
|
| 345 |
+
```
|
| 346 |
+
to allow notation such as `fun (a, b) => a + b`, where `(a, b)` should be treated as a pattern.
|
| 347 |
+
The result is a pair `(explicitBinders, newBody)`, where `explicitBinders` is syntax of the form
|
| 348 |
+
```
|
| 349 |
+
`(` ident `:` term `)`
|
| 350 |
+
```
|
| 351 |
+
which can be elaborated using `elabBinders`, and `newBody` is the updated `body` syntax.
|
| 352 |
+
We update the `body` syntax when expanding the pattern notation.
|
| 353 |
+
Example: `fun (a, b) => a + b` expands into `fun _a_1 => match _a_1 with | (a, b) => a + b`.
|
| 354 |
+
See local function `processAsPattern` at `expandFunBindersAux`.
|
| 355 |
+
|
| 356 |
+
The resulting `Bool` is true if a pattern was found. We use it "mark" a macro expansion. -/
|
| 357 |
+
partial def expandFunBinders (binders : Array Syntax) (body : Syntax) : MacroM (Array Syntax Γ Syntax Γ Bool) :=
|
| 358 |
+
let rec loop (body : Syntax) (i : Nat) (newBinders : Array Syntax) := do
|
| 359 |
+
if h : i < binders.size then
|
| 360 |
+
let binder := binders[i]
|
| 361 |
+
let processAsPattern : Unit β MacroM (Array Syntax Γ Syntax Γ Bool) := fun _ => do
|
| 362 |
+
let pattern := binder
|
| 363 |
+
let major β mkFreshIdent binder
|
| 364 |
+
let (binders, newBody, _) β loop body (i+1) (newBinders.push $ mkExplicitBinder major (mkHole binder))
|
| 365 |
+
let newBody β `(match $major:ident with | $pattern => $newBody)
|
| 366 |
+
pure (binders, newBody, true)
|
| 367 |
+
match binder.getKind with
|
| 368 |
+
| ``Lean.Parser.Term.implicitBinder
|
| 369 |
+
| ``Lean.Parser.Term.strictImplicitBinder
|
| 370 |
+
| ``Lean.Parser.Term.instBinder
|
| 371 |
+
| ``Lean.Parser.Term.explicitBinder
|
| 372 |
+
| ``Lean.Parser.Term.hole | `ident => loop body (i+1) (newBinders.push binder)
|
| 373 |
+
| ``Lean.Parser.Term.paren =>
|
| 374 |
+
let term := binder[1]
|
| 375 |
+
match (β getFunBinderIds? term) with
|
| 376 |
+
| some idents =>
|
| 377 |
+
-- `fun (x ...) ...` ~> `fun (x : _) ...`
|
| 378 |
+
-- Interpret `(x ...)` as sequence of binders instead of pattern only if none of the idents
|
| 379 |
+
-- are defined in the global scope. Technically, it would be sufficient to only check the
|
| 380 |
+
-- first ident to be sure that the syntax cannot possibly be a valid pattern. However, for
|
| 381 |
+
-- consistency we apply the same check to all idents so that the possibility of shadowing
|
| 382 |
+
-- a global decl is identical for all of them.
|
| 383 |
+
if (β idents.allM fun ident => return List.isEmpty (β Macro.resolveGlobalName ident.getId)) then
|
| 384 |
+
loop body (i+1) (newBinders ++ idents.map (mkExplicitBinder Β· (mkHole binder)))
|
| 385 |
+
else
|
| 386 |
+
processAsPattern ()
|
| 387 |
+
| none => processAsPattern ()
|
| 388 |
+
| ``Lean.Parser.Term.typeAscription =>
|
| 389 |
+
let term := binder[1]
|
| 390 |
+
let type := binder[3].getOptional?.getD (mkHole binder)
|
| 391 |
+
match (β getFunBinderIds? term) with
|
| 392 |
+
| some idents => loop body (i+1) (newBinders ++ idents.map (fun ident => mkExplicitBinder ident type))
|
| 393 |
+
| none => processAsPattern ()
|
| 394 |
+
| _ => processAsPattern ()
|
| 395 |
+
else
|
| 396 |
+
pure (newBinders, body, false)
|
| 397 |
+
loop body 0 #[]
|
| 398 |
+
|
| 399 |
+
namespace FunBinders
|
| 400 |
+
|
| 401 |
+
structure State where
|
| 402 |
+
fvars : Array Expr := #[]
|
| 403 |
+
lctx : LocalContext
|
| 404 |
+
localInsts : LocalInstances
|
| 405 |
+
expectedType? : Option Expr := none
|
| 406 |
+
|
| 407 |
+
private def propagateExpectedType (fvar : Expr) (fvarType : Expr) (s : State) : TermElabM State := do
|
| 408 |
+
match s.expectedType? with
|
| 409 |
+
| none => pure s
|
| 410 |
+
| some expectedType =>
|
| 411 |
+
let expectedType β whnfForall expectedType
|
| 412 |
+
match expectedType with
|
| 413 |
+
| .forallE _ d b _ =>
|
| 414 |
+
discard <| isDefEq fvarType d
|
| 415 |
+
let b := b.instantiate1 fvar
|
| 416 |
+
return { s with expectedType? := some b }
|
| 417 |
+
| _ =>
|
| 418 |
+
return { s with expectedType? := none }
|
| 419 |
+
|
| 420 |
+
private partial def elabFunBinderViews (binderViews : Array BinderView) (i : Nat) (s : State) : TermElabM State := do
|
| 421 |
+
if h : i < binderViews.size then
|
| 422 |
+
let binderView := binderViews[i]
|
| 423 |
+
ensureAtomicBinderName binderView
|
| 424 |
+
withRef binderView.type <| withLCtx s.lctx s.localInsts do
|
| 425 |
+
let type β elabType binderView.type
|
| 426 |
+
registerFailedToInferBinderTypeInfo type binderView.type
|
| 427 |
+
let fvarId β mkFreshFVarId
|
| 428 |
+
let fvar := mkFVar fvarId
|
| 429 |
+
let s := { s with fvars := s.fvars.push fvar }
|
| 430 |
+
let id := binderView.id.getId
|
| 431 |
+
let kind := kindOfBinderName id
|
| 432 |
+
/-
|
| 433 |
+
We do **not** want to support default and auto arguments in lambda abstractions.
|
| 434 |
+
Example: `fun (x : Nat := 10) => x+1`.
|
| 435 |
+
We do not believe this is an useful feature, and it would complicate the logic here.
|
| 436 |
+
-/
|
| 437 |
+
let lctx := s.lctx.mkLocalDecl fvarId id type binderView.bi kind
|
| 438 |
+
addTermInfo' (lctx? := some lctx) (isBinder := true) binderView.ref fvar
|
| 439 |
+
let s β withRef binderView.id <| propagateExpectedType fvar type s
|
| 440 |
+
let s := { s with lctx }
|
| 441 |
+
match β isClass? type, kind with
|
| 442 |
+
| some className, .default =>
|
| 443 |
+
let localInsts := s.localInsts.push { className, fvar := mkFVar fvarId }
|
| 444 |
+
elabFunBinderViews binderViews (i+1) { s with localInsts }
|
| 445 |
+
| _, _ => elabFunBinderViews binderViews (i+1) s
|
| 446 |
+
else
|
| 447 |
+
pure s
|
| 448 |
+
|
| 449 |
+
partial def elabFunBindersAux (binders : Array Syntax) (i : Nat) (s : State) : TermElabM State := do
|
| 450 |
+
if h : i < binders.size then
|
| 451 |
+
let binderViews β toBinderViews binders[i]
|
| 452 |
+
let s β elabFunBinderViews binderViews 0 s
|
| 453 |
+
elabFunBindersAux binders (i+1) s
|
| 454 |
+
else
|
| 455 |
+
pure s
|
| 456 |
+
|
| 457 |
+
end FunBinders
|
| 458 |
+
|
| 459 |
+
def elabFunBinders (binders : Array Syntax) (expectedType? : Option Expr) (x : Array Expr β Option Expr β TermElabM οΏ½οΏ½) : TermElabM Ξ± :=
|
| 460 |
+
if binders.isEmpty then
|
| 461 |
+
x #[] expectedType?
|
| 462 |
+
else do
|
| 463 |
+
let lctx β getLCtx
|
| 464 |
+
let localInsts β getLocalInstances
|
| 465 |
+
let s β FunBinders.elabFunBindersAux binders 0 { lctx, localInsts, expectedType? }
|
| 466 |
+
withLCtx s.lctx s.localInsts do
|
| 467 |
+
x s.fvars s.expectedType?
|
| 468 |
+
|
| 469 |
+
def expandWhereDecls (whereDecls : Syntax) (body : Syntax) : MacroM Syntax :=
|
| 470 |
+
match whereDecls with
|
| 471 |
+
| `(whereDecls|where $[$_:whereFinally]?) => `($body)
|
| 472 |
+
| `(whereDecls|where $[$decls:letRecDecl];* $[$_:whereFinally]?) => `(let rec $decls:letRecDecl,*; $body)
|
| 473 |
+
| _ => Macro.throwUnsupported
|
| 474 |
+
|
| 475 |
+
def expandWhereDeclsOpt (whereDeclsOpt : Syntax) (body : Syntax) : MacroM Syntax :=
|
| 476 |
+
if whereDeclsOpt.isNone then
|
| 477 |
+
return body
|
| 478 |
+
else
|
| 479 |
+
expandWhereDecls whereDeclsOpt[0] body
|
| 480 |
+
|
| 481 |
+
/--
|
| 482 |
+
Helper function for `expandMatchAltsIntoMatch`.
|
| 483 |
+
-/
|
| 484 |
+
private def expandMatchAltsIntoMatchAux (matchAlts : Syntax) (isTactic : Bool) (useExplicit : Bool) : Nat β Array Syntax β Array Ident β MacroM Syntax
|
| 485 |
+
| 0, discrs, xs => do
|
| 486 |
+
if isTactic then
|
| 487 |
+
`(tactic|match $[$discrs:term],* with $matchAlts:matchAlts)
|
| 488 |
+
else
|
| 489 |
+
let stx β `(match $[$discrs:term],* with $matchAlts:matchAlts)
|
| 490 |
+
clearInMatch stx xs
|
| 491 |
+
| n+1, discrs, xs => withFreshMacroScope do
|
| 492 |
+
let x β `(x) -- If this were implementation-detail, the `contradiction` tactic used by match would not find it.
|
| 493 |
+
let d β `(@$x:ident)
|
| 494 |
+
let body β expandMatchAltsIntoMatchAux matchAlts isTactic useExplicit n (discrs.push d) (xs.push x)
|
| 495 |
+
if isTactic then
|
| 496 |
+
`(tactic| intro $x:term; $body:tactic)
|
| 497 |
+
else if useExplicit then
|
| 498 |
+
`(@fun $x => $body)
|
| 499 |
+
else
|
| 500 |
+
`(fun $x => $body)
|
| 501 |
+
|
| 502 |
+
/--
|
| 503 |
+
Expand `matchAlts` syntax into a full `match`-expression.
|
| 504 |
+
Example:
|
| 505 |
+
```
|
| 506 |
+
| 0, true => alt_1
|
| 507 |
+
| i, _ => alt_2
|
| 508 |
+
```
|
| 509 |
+
expands into (for tactic == false)
|
| 510 |
+
```
|
| 511 |
+
fun x_1 x_2 =>
|
| 512 |
+
match @x_1, @x_2 with
|
| 513 |
+
| 0, true => alt_1
|
| 514 |
+
| i, _ => alt_2
|
| 515 |
+
```
|
| 516 |
+
and (for tactic == true)
|
| 517 |
+
```
|
| 518 |
+
intro x_1; intro x_2;
|
| 519 |
+
match @x_1, @x_2 with
|
| 520 |
+
| 0, true => alt_1
|
| 521 |
+
| i, _ => alt_2
|
| 522 |
+
```
|
| 523 |
+
|
| 524 |
+
If `useExplicit = true`, we add a `@` before `fun` to disable implicit lambdas. We disable them when processing `let` and `let rec` declarations
|
| 525 |
+
to make sure the behavior is consistent with top-level declarations where we can write
|
| 526 |
+
```
|
| 527 |
+
def f : {Ξ± : Type} β Ξ± β Ξ±
|
| 528 |
+
| _, a => a
|
| 529 |
+
```
|
| 530 |
+
We use `useExplicit = false` when we are elaborating the `fun | ... => ... | ...` notation. See issue #1132.
|
| 531 |
+
If `@fun` is used with this notation, the we set `useExplicit = true`.
|
| 532 |
+
We also use `useExplicit = false` when processing `instance ... where` notation declarations. The motivation is to have compact declarations such as
|
| 533 |
+
```
|
| 534 |
+
instance [Alternative m] : MonadLiftT Option m where
|
| 535 |
+
monadLift -- We don't want to provide the implicit arguments of `monadLift` here. One should use `monadLift := @fun ...` if they want to provide them.
|
| 536 |
+
| some a => pure a
|
| 537 |
+
| none => failure
|
| 538 |
+
```
|
| 539 |
+
|
| 540 |
+
Remark: we add `@` at discriminants to make sure we don't consume implicit arguments, and to make the behavior consistent with `fun`.
|
| 541 |
+
Example:
|
| 542 |
+
```
|
| 543 |
+
inductive T : Type 1 :=
|
| 544 |
+
| mkT : (forall {a : Type}, a -> a) -> T
|
| 545 |
+
|
| 546 |
+
def makeT (f : forall {a : Type}, a -> a) : T :=
|
| 547 |
+
mkT f
|
| 548 |
+
|
| 549 |
+
def makeT' : (forall {a : Type}, a -> a) -> T
|
| 550 |
+
| f => mkT f
|
| 551 |
+
```
|
| 552 |
+
The two definitions should be elaborated without errors and be equivalent.
|
| 553 |
+
-/
|
| 554 |
+
def expandMatchAltsIntoMatch (ref : Syntax) (matchAlts : Syntax) (useExplicit := true) : MacroM Syntax :=
|
| 555 |
+
withRef ref <| expandMatchAltsIntoMatchAux matchAlts (isTactic := false) (useExplicit := useExplicit) (getMatchAltsNumPatterns matchAlts) #[] #[]
|
| 556 |
+
|
| 557 |
+
def expandMatchAltsIntoMatchTactic (ref : Syntax) (matchAlts : Syntax) : MacroM Syntax :=
|
| 558 |
+
withRef ref <| expandMatchAltsIntoMatchAux matchAlts (isTactic := true) (useExplicit := false) (getMatchAltsNumPatterns matchAlts) #[] #[]
|
| 559 |
+
|
| 560 |
+
/--
|
| 561 |
+
Sanity-checks the number of patterns in each alternative of a definition by pattern matching.
|
| 562 |
+
Specifically, verifies that all alternatives have the same number of patterns and that the number
|
| 563 |
+
of patterns is upper-bounded by the number of (dependent) arrows in the expected type.
|
| 564 |
+
|
| 565 |
+
Note: This function assumes that the number of patterns in the first alternative will be equal to
|
| 566 |
+
`numDiscrs` (since we use the first alternative to infer the arity of the generated matcher in
|
| 567 |
+
`getMatchAltsNumPatterns`).
|
| 568 |
+
-/
|
| 569 |
+
private def checkMatchAltPatternCounts (matchAlts : Syntax) (numDiscrs : Nat) (expectedType : Expr)
|
| 570 |
+
: MetaM Unit := do
|
| 571 |
+
let sepPats (pats : List Syntax) := MessageData.joinSep (pats.map toMessageData) ", "
|
| 572 |
+
let maxDiscrs? β forallTelescopeReducing expectedType fun xs e =>
|
| 573 |
+
if e.getAppFn.isMVar then pure none else pure (some xs.size)
|
| 574 |
+
let matchAltViews := matchAlts[0].getArgs.filterMap getMatchAlt
|
| 575 |
+
let numPatternsStr (n : Nat) := s!"{n} {if n == 1 then "pattern" else "patterns"}"
|
| 576 |
+
if h : matchAltViews.size > 0 then
|
| 577 |
+
if let some maxDiscrs := maxDiscrs? then
|
| 578 |
+
if numDiscrs > maxDiscrs then
|
| 579 |
+
if maxDiscrs == 0 then
|
| 580 |
+
throwErrorAt matchAltViews[0].lhs m!"Cannot define a value of type{indentExpr expectedType}\n\
|
| 581 |
+
by pattern matching because it is not a function type"
|
| 582 |
+
else
|
| 583 |
+
throwErrorAt matchAltViews[0].lhs m!"Too many patterns in match alternative: \
|
| 584 |
+
At most {numPatternsStr maxDiscrs} expected in a definition of type {indentExpr expectedType}\n\
|
| 585 |
+
but found {numDiscrs}:{indentD <| sepPats matchAltViews[0].patterns.toList}"
|
| 586 |
+
-- Catch inconsistencies between pattern counts here so that we can report them as "inconsistent"
|
| 587 |
+
-- rather than as "too many" or "too few" (as the `match` elaborator does)
|
| 588 |
+
for view in matchAltViews do
|
| 589 |
+
let numPats := view.patterns.size
|
| 590 |
+
if numPats != numDiscrs then
|
| 591 |
+
let origPats := sepPats matchAltViews[0].patterns.toList
|
| 592 |
+
let pats := sepPats view.patterns.toList
|
| 593 |
+
throwErrorAt view.lhs m!"Inconsistent number of patterns in match alternatives: This \
|
| 594 |
+
alternative contains {numPatternsStr numPats}:{indentD pats}\n\
|
| 595 |
+
but a preceding alternative contains {numDiscrs}:{indentD origPats}"
|
| 596 |
+
|
| 597 |
+
/--
|
| 598 |
+
Similar to `expandMatchAltsIntoMatch`, but supports an optional `where` clause.
|
| 599 |
+
|
| 600 |
+
Expand `matchAltsWhereDecls` into `let rec` + `match`-expression.
|
| 601 |
+
Example
|
| 602 |
+
```
|
| 603 |
+
| 0, true => ... f 0 ...
|
| 604 |
+
| i, _ => ... f i + g i ...
|
| 605 |
+
where
|
| 606 |
+
f x := g x + 1
|
| 607 |
+
|
| 608 |
+
g : Nat β Nat
|
| 609 |
+
| 0 => 1
|
| 610 |
+
| x+1 => f x
|
| 611 |
+
```
|
| 612 |
+
expands into
|
| 613 |
+
```
|
| 614 |
+
fux x_1 x_2 =>
|
| 615 |
+
let rec
|
| 616 |
+
f x := g x + 1,
|
| 617 |
+
g : Nat β Nat
|
| 618 |
+
| 0 => 1
|
| 619 |
+
| x+1 => f x
|
| 620 |
+
match x_1, x_2 with
|
| 621 |
+
| 0, true => ... f 0 ...
|
| 622 |
+
| i, _ => ... f i + g i ...
|
| 623 |
+
```
|
| 624 |
+
-/
|
| 625 |
+
def expandMatchAltsWhereDecls (matchAltsWhereDecls : Syntax) (expectedType : Expr) : TermElabM Syntax :=
|
| 626 |
+
let matchAlts := matchAltsWhereDecls[0]
|
| 627 |
+
-- matchAltsWhereDecls[1] is the termination hints, collected elsewhere
|
| 628 |
+
let whereDeclsOpt := matchAltsWhereDecls[2]
|
| 629 |
+
let rec loop (i : Nat) (discrs : Array Syntax) : TermElabM Syntax :=
|
| 630 |
+
match i with
|
| 631 |
+
| 0 => do
|
| 632 |
+
checkMatchAltPatternCounts matchAlts discrs.size expectedType
|
| 633 |
+
let matchStx β `(match $[@$discrs:term],* with $matchAlts:matchAlts)
|
| 634 |
+
liftMacroM do
|
| 635 |
+
let matchStx β clearInMatch matchStx discrs
|
| 636 |
+
if whereDeclsOpt.isNone then
|
| 637 |
+
return matchStx
|
| 638 |
+
else
|
| 639 |
+
expandWhereDeclsOpt whereDeclsOpt matchStx
|
| 640 |
+
| n+1 => withFreshMacroScope do
|
| 641 |
+
let body β loop n (discrs.push (β `(x)))
|
| 642 |
+
`(@fun x => $body)
|
| 643 |
+
loop (getMatchAltsNumPatterns matchAlts) #[]
|
| 644 |
+
|
| 645 |
+
@[builtin_macro Parser.Term.fun] partial def expandFun : Macro
|
| 646 |
+
| `(fun $binders* : $ty => $body) => do
|
| 647 |
+
let binders β binders.mapM (expandSimpleBinderWithType ty)
|
| 648 |
+
`(fun $binders* => $body)
|
| 649 |
+
| `(fun $binders* => $body) => do -- if there is a type ascription, we assume all binders are already simple
|
| 650 |
+
let (binders, body, expandedPattern) β expandFunBinders binders body
|
| 651 |
+
if expandedPattern then
|
| 652 |
+
`(fun $binders* => $body)
|
| 653 |
+
else
|
| 654 |
+
Macro.throwUnsupported
|
| 655 |
+
| stx@`(fun $m:matchAlts) => expandMatchAltsIntoMatch stx m (useExplicit := false)
|
| 656 |
+
| _ => Macro.throwUnsupported
|
| 657 |
+
|
| 658 |
+
@[builtin_macro Parser.Term.explicit] partial def expandExplicitFun : Macro := fun stx =>
|
| 659 |
+
match stx with
|
| 660 |
+
| `(@fun $m:matchAlts) => expandMatchAltsIntoMatch stx[1] m (useExplicit := true)
|
| 661 |
+
| _ => Macro.throwUnsupported
|
| 662 |
+
|
| 663 |
+
open Lean.Elab.Term.Quotation in
|
| 664 |
+
@[builtin_quot_precheck Lean.Parser.Term.fun] def precheckFun : Precheck
|
| 665 |
+
| `(fun $binders* $[: $ty?]? => $body) => do
|
| 666 |
+
let (binders, body, _) β liftMacroM <| expandFunBinders binders body
|
| 667 |
+
let mut ids := #[]
|
| 668 |
+
for b in binders do
|
| 669 |
+
for v in β toBinderViews b do
|
| 670 |
+
Quotation.withNewLocals ids <| precheck v.type
|
| 671 |
+
ids := ids.push v.id.getId
|
| 672 |
+
Quotation.withNewLocals ids <| precheck body
|
| 673 |
+
| _ => throwUnsupportedSyntax
|
| 674 |
+
|
| 675 |
+
@[builtin_term_elab Β«funΒ»] partial def elabFun : TermElab := fun stx expectedType? =>
|
| 676 |
+
match stx with
|
| 677 |
+
| `(fun $binders* => $body) => do
|
| 678 |
+
-- We can assume all `match` binders have been iteratively expanded by the above macro here, though
|
| 679 |
+
-- we still need to call `expandFunBinders` once to obtain `binders` in a normal form
|
| 680 |
+
-- expected by `elabFunBinder`.
|
| 681 |
+
let (binders, body, _) β liftMacroM <| expandFunBinders binders body
|
| 682 |
+
elabFunBinders binders expectedType? fun xs expectedType? => do
|
| 683 |
+
/- We ensure the expectedType here since it will force coercions to be applied if needed.
|
| 684 |
+
If we just use `elabTerm`, then we will need to a coercion `Coe (Ξ± β Ξ²) (Ξ± β Ξ΄)` whenever there is a coercion `Coe Ξ² Ξ΄`,
|
| 685 |
+
and another instance for the dependent version. -/
|
| 686 |
+
let e β elabTermEnsuringType body expectedType?
|
| 687 |
+
mkLambdaFVars xs e
|
| 688 |
+
| _ => throwUnsupportedSyntax
|
| 689 |
+
|
| 690 |
+
/--
|
| 691 |
+
Configuration for `let` elaboration.
|
| 692 |
+
-/
|
| 693 |
+
structure LetConfig where
|
| 694 |
+
/-- Elaborate as a nondependent `let` (a `have`). -/
|
| 695 |
+
nondep : Bool := false
|
| 696 |
+
/-- Eliminate the `let` if it is unused by the body. -/
|
| 697 |
+
usedOnly : Bool := false
|
| 698 |
+
/-- Zeta reduces (inlines) the `let`. -/
|
| 699 |
+
zeta : Bool := false
|
| 700 |
+
/-- Postpone elaboration of the value until after the body is elaborated. -/
|
| 701 |
+
postponeValue : Bool := false
|
| 702 |
+
/-- Generalize the value from the expected type when elaborating the body. -/
|
| 703 |
+
generalize : Bool := false
|
| 704 |
+
/-- For `let x := v; b`, adds `eq : x = v` to the context. -/
|
| 705 |
+
eq? : Option Ident := none
|
| 706 |
+
|
| 707 |
+
def LetConfig.setFrom (config : LetConfig) (key : Syntax) (val : Bool) : LetConfig :=
|
| 708 |
+
if key.isOfKind ``Parser.Term.letOptNondep then
|
| 709 |
+
{ config with nondep := val }
|
| 710 |
+
else if key.isOfKind ``Parser.Term.letOptUsedOnly then
|
| 711 |
+
{ config with usedOnly := val }
|
| 712 |
+
else if key.isOfKind ``Parser.Term.letOptZeta then
|
| 713 |
+
{ config with zeta := val }
|
| 714 |
+
else if key.isOfKind ``Parser.Term.letOptPostponeValue then
|
| 715 |
+
{ config with postponeValue := val }
|
| 716 |
+
else if key.isOfKind ``Parser.Term.letOptGeneralize then
|
| 717 |
+
{ config with generalize := val }
|
| 718 |
+
else
|
| 719 |
+
config
|
| 720 |
+
|
| 721 |
+
/--
|
| 722 |
+
Interprets a `Parser.Term.letConfig`.
|
| 723 |
+
-/
|
| 724 |
+
def mkLetConfig (letConfig : Syntax) (initConfig : LetConfig) : TermElabM LetConfig := do
|
| 725 |
+
let mut config := initConfig
|
| 726 |
+
unless letConfig.isOfKind ``Parser.Term.letConfig do
|
| 727 |
+
return config
|
| 728 |
+
for item in letConfig[0].getArgs do
|
| 729 |
+
match item with
|
| 730 |
+
| `(letPosOpt| +$opt:letOpts) => config := config.setFrom opt.raw[0] true
|
| 731 |
+
| `(letNegOpt| -$opt:letOpts) => config := config.setFrom opt.raw[0] false
|
| 732 |
+
| `(letOptEq| (eq := $n:ident)) => config := { config with eq? := n }
|
| 733 |
+
| `(letOptEq| (eq := $b)) => config := { config with eq? := mkIdentFrom b (canonical := true) (β mkFreshBinderNameForTactic `h) }
|
| 734 |
+
| _ => pure ()
|
| 735 |
+
return config
|
| 736 |
+
|
| 737 |
+
/--
|
| 738 |
+
The default elaboration order is `binders`, `typeStx`, `valStx`, and `body`.
|
| 739 |
+
If `config.postponeValue == true`, then we use the order `binders`, `typeStx`, `body`, and `valStx`.
|
| 740 |
+
If `config.generalize == true`, then the value is abstracted from the expected type when elaborating the body.
|
| 741 |
+
-/
|
| 742 |
+
def elabLetDeclAux (id : Syntax) (binders : Array Syntax) (typeStx : Syntax) (valStx : Syntax) (body : Syntax)
|
| 743 |
+
(expectedType? : Option Expr) (config : LetConfig) : TermElabM Expr := do
|
| 744 |
+
if config.generalize then
|
| 745 |
+
if config.postponeValue then
|
| 746 |
+
throwError "`+postponeValue` and `+generalize` are incompatible"
|
| 747 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 748 |
+
let (type, val, binders) β elabBindersEx binders fun xs => do
|
| 749 |
+
let (binders, fvars) := xs.unzip
|
| 750 |
+
/-
|
| 751 |
+
We use `withSynthesize` to ensure that any postponed elaboration problem
|
| 752 |
+
and nested tactics in `type` are resolved before elaborating `val`.
|
| 753 |
+
Resolved: we want to avoid synthetic opaque metavariables in `type`.
|
| 754 |
+
Recall that this kind of metavariable is non-assignable, and `isDefEq`
|
| 755 |
+
may waste a lot of time unfolding declarations before failing.
|
| 756 |
+
See issue #4051 for an example.
|
| 757 |
+
|
| 758 |
+
Here is the analysis for issue #4051.
|
| 759 |
+
- Given `have x : type := value; body`, we were previously elaborating `value` even
|
| 760 |
+
if `type` contained postponed elaboration problems.
|
| 761 |
+
- Moreover, the metavariables in `type` corresponding to postponed elaboration
|
| 762 |
+
problems cannot be assigned by `isDefEq` since the elaborator is supposed to assign them.
|
| 763 |
+
- Then, when checking whether type of `value` is definitionally equal to `type`,
|
| 764 |
+
a very long-time was spent unfolding a bunch of declarations before it failed.
|
| 765 |
+
In #4051, it was unfolding `Array.swaps` which is defined by well-founded recursion.
|
| 766 |
+
After the failure, the elaborator inserted a postponed coercion
|
| 767 |
+
that would be resolved later as soon as the types don't have unassigned metavariables.
|
| 768 |
+
|
| 769 |
+
We use `postpone := .partial` to allow type class (TC) resolution problems to be postponed
|
| 770 |
+
Recall that TC resolution does **not** produce synthetic opaque metavariables.
|
| 771 |
+
-/
|
| 772 |
+
let type β withSynthesize (postpone := .partial) <| elabType typeStx
|
| 773 |
+
let letMsg := if config.nondep then "have" else "let"
|
| 774 |
+
registerCustomErrorIfMVar type typeStx m!"failed to infer '{letMsg}' declaration type"
|
| 775 |
+
registerLevelMVarErrorExprInfo type typeStx m!"failed to infer universe levels in '{letMsg}' declaration type"
|
| 776 |
+
if config.postponeValue then
|
| 777 |
+
let type β mkForallFVars fvars type
|
| 778 |
+
let val β mkFreshExprMVar type
|
| 779 |
+
pure (type, val, binders)
|
| 780 |
+
else
|
| 781 |
+
let val β elabTermEnsuringType valStx type
|
| 782 |
+
let type β mkForallFVars fvars type
|
| 783 |
+
/- By default `mkLambdaFVars` and `mkLetFVars` create binders only for let-declarations that are actually used
|
| 784 |
+
in the body. This generates counterintuitive behavior in the elaborator since users will not be notified
|
| 785 |
+
about holes such as
|
| 786 |
+
```
|
| 787 |
+
def ex : Nat :=
|
| 788 |
+
let x := _
|
| 789 |
+
42
|
| 790 |
+
```
|
| 791 |
+
-/
|
| 792 |
+
let val β mkLambdaFVars fvars val (usedLetOnly := false)
|
| 793 |
+
pure (type, val, binders)
|
| 794 |
+
let kind := kindOfBinderName id.getId
|
| 795 |
+
trace[Elab.let.decl] "{id.getId} : {type} := {val}"
|
| 796 |
+
let result β
|
| 797 |
+
withLetDecl id.getId (kind := kind) type val (nondep := config.nondep) fun x => do
|
| 798 |
+
let elabBody : TermElabM Expr := do
|
| 799 |
+
let mut expectedType? := expectedType?
|
| 800 |
+
if config.generalize then
|
| 801 |
+
let throwNoType := throwError "failed to elaborate with `+generalize`, expected type is not available"
|
| 802 |
+
let some expectedType := expectedType? | throwNoType
|
| 803 |
+
let expectedType β instantiateMVars expectedType
|
| 804 |
+
if expectedType.getAppFn.isMVar then throwNoType
|
| 805 |
+
let motiveBody β kabstract expectedType (β instantiateMVars val)
|
| 806 |
+
let motive := motiveBody.instantiate1 x
|
| 807 |
+
-- When `config.nondep` is false, then `motive` will be definitionally equal to `expectedType`.
|
| 808 |
+
-- Type correctness only needs to be checked in the `nondep` case:
|
| 809 |
+
if config.nondep then
|
| 810 |
+
unless (β isTypeCorrect motive) do
|
| 811 |
+
throwError "failed to elaborate with `+generalize`, generalized expected type is not type correct:{indentD motive}"
|
| 812 |
+
expectedType? := motive
|
| 813 |
+
elabTermEnsuringType body expectedType? >>= instantiateMVars
|
| 814 |
+
addLocalVarInfo id x
|
| 815 |
+
match config.eq? with
|
| 816 |
+
| none =>
|
| 817 |
+
let body β elabBody
|
| 818 |
+
if config.zeta then
|
| 819 |
+
pure <| (β body.abstractM #[x]).instantiate1 val
|
| 820 |
+
else
|
| 821 |
+
mkLetFVars #[x] body (usedLetOnly := config.usedOnly) (generalizeNondepLet := false)
|
| 822 |
+
| some h =>
|
| 823 |
+
let hTy β mkEq x val
|
| 824 |
+
withLetDecl h.getId hTy (β mkEqRefl x) (nondep := true) fun h' => do
|
| 825 |
+
addLocalVarInfo h h'
|
| 826 |
+
let body β elabBody
|
| 827 |
+
if config.zeta then
|
| 828 |
+
pure <| (β body.abstractM #[x, h']).instantiateRev #[val, β mkEqRefl val]
|
| 829 |
+
else if config.nondep then
|
| 830 |
+
-- TODO(kmill): Think more about how to encode this case.
|
| 831 |
+
-- Currently we produce `(fun (x : Ξ±) (h : x = val) => b) val rfl`.
|
| 832 |
+
-- N.B. the nondep lets become lambdas here.
|
| 833 |
+
let f β mkLambdaFVars #[x, h'] body
|
| 834 |
+
return mkApp2 f val (β mkEqRefl val)
|
| 835 |
+
else
|
| 836 |
+
mkLetFVars #[x, h'] body (usedLetOnly := config.usedOnly) (generalizeNondepLet := false)
|
| 837 |
+
if config.postponeValue then
|
| 838 |
+
forallBoundedTelescope type binders.size fun xs type => do
|
| 839 |
+
-- the original `fvars` from above are gone, so add back info manually
|
| 840 |
+
for b in binders, x in xs do
|
| 841 |
+
addLocalVarInfo b x
|
| 842 |
+
let valResult β elabTermEnsuringType valStx type
|
| 843 |
+
let valResult β mkLambdaFVars xs valResult (usedLetOnly := false)
|
| 844 |
+
unless (β isDefEq val valResult) do
|
| 845 |
+
throwError "unexpected error when elaborating 'let'"
|
| 846 |
+
pure result
|
| 847 |
+
|
| 848 |
+
structure LetIdDeclView where
|
| 849 |
+
id : Syntax
|
| 850 |
+
binders : Array Syntax
|
| 851 |
+
type : Syntax
|
| 852 |
+
value : Syntax
|
| 853 |
+
|
| 854 |
+
def mkLetIdDeclView (letIdDecl : Syntax) : LetIdDeclView :=
|
| 855 |
+
/-
|
| 856 |
+
def letId := leading_parser binderIdent <|> hygieneInfo
|
| 857 |
+
def letIdBinder := binderIdent <|> bracketedBinder
|
| 858 |
+
def letIdLhs := letId >> many letIdBinder >> optType
|
| 859 |
+
def letIdDecl := leading_parser letIdLhs >> " := " >> termParser
|
| 860 |
+
-/
|
| 861 |
+
let letId := letIdDecl[0]
|
| 862 |
+
let id :=
|
| 863 |
+
if letId[0].isOfKind hygieneInfoKind then
|
| 864 |
+
HygieneInfo.mkIdent letId[0] `this (canonical := true)
|
| 865 |
+
else
|
| 866 |
+
-- Assumed to be binderIdent
|
| 867 |
+
letId[0]
|
| 868 |
+
let binders := letIdDecl[1].getArgs
|
| 869 |
+
let optType := letIdDecl[2]
|
| 870 |
+
let type := expandOptType id optType
|
| 871 |
+
let value := letIdDecl[4]
|
| 872 |
+
{ id, binders, type, value }
|
| 873 |
+
|
| 874 |
+
def expandLetEqnsDecl (letDecl : Syntax) (useExplicit := true) : MacroM Syntax := do
|
| 875 |
+
let ref := letDecl
|
| 876 |
+
let matchAlts := letDecl[3]
|
| 877 |
+
let val β expandMatchAltsIntoMatch ref matchAlts (useExplicit := useExplicit)
|
| 878 |
+
return mkNode `Lean.Parser.Term.letIdDecl #[letDecl[0], letDecl[1], letDecl[2], mkAtomFrom ref " := ", val]
|
| 879 |
+
|
| 880 |
+
def elabLetDeclCore (stx : Syntax) (expectedType? : Option Expr) (initConfig : LetConfig) : TermElabM Expr := do
|
| 881 |
+
let (config, declIdx) β if stx[1].isOfKind ``Parser.Term.letConfig then
|
| 882 |
+
pure (β mkLetConfig stx[1] initConfig, 2)
|
| 883 |
+
else
|
| 884 |
+
pure (initConfig, 1)
|
| 885 |
+
let letDecl := stx[declIdx][0]
|
| 886 |
+
let body := stx[declIdx + 2]
|
| 887 |
+
if letDecl.getKind == ``Lean.Parser.Term.letIdDecl then
|
| 888 |
+
let { id, binders, type, value } := mkLetIdDeclView letDecl
|
| 889 |
+
let id β if id.isIdent then pure id else mkFreshIdent id (canonical := true)
|
| 890 |
+
elabLetDeclAux id binders type value body expectedType? config
|
| 891 |
+
else if letDecl.getKind == ``Lean.Parser.Term.letPatDecl then
|
| 892 |
+
-- node `Lean.Parser.Term.letPatDecl $ try (termParser >> pushNone >> optType >> " := ") >> termParser
|
| 893 |
+
let pat := letDecl[0]
|
| 894 |
+
let optType := letDecl[2]
|
| 895 |
+
let val := letDecl[4]
|
| 896 |
+
if pat.getKind == ``Parser.Term.hole then
|
| 897 |
+
-- `let _ := ...` should be treated as a `letIdDecl`
|
| 898 |
+
let id β mkFreshIdent pat (canonical := true)
|
| 899 |
+
let type := expandOptType id optType
|
| 900 |
+
elabLetDeclAux id #[] type val body expectedType? config
|
| 901 |
+
else
|
| 902 |
+
if config.postponeValue then
|
| 903 |
+
throwError "`+deferValue` with patterns is not allowed"
|
| 904 |
+
if config.usedOnly then
|
| 905 |
+
throwError "`+usedOnly` with patterns is not allowed"
|
| 906 |
+
if config.zeta then
|
| 907 |
+
throwError "`+zeta` with patterns is not allowed"
|
| 908 |
+
-- We are currently ignore `config.nondep` when patterns are used.
|
| 909 |
+
-- We are also currently ignoring `config.generalize`.
|
| 910 |
+
let val β if optType.isNone then
|
| 911 |
+
`($val:term)
|
| 912 |
+
else
|
| 913 |
+
let type := optType[0][1]
|
| 914 |
+
`(($val:term : $type))
|
| 915 |
+
let stxNew β if let some h := config.eq? then
|
| 916 |
+
`(match $h:ident : $val:term with | $pat => $body)
|
| 917 |
+
else
|
| 918 |
+
`(match $val:term with | $pat => $body)
|
| 919 |
+
withMacroExpansion stx stxNew <| elabTerm stxNew expectedType?
|
| 920 |
+
else if letDecl.getKind == ``Lean.Parser.Term.letEqnsDecl then
|
| 921 |
+
let letDeclIdNew β liftMacroM <| expandLetEqnsDecl letDecl
|
| 922 |
+
let declNew := stx[declIdx].setArg 0 letDeclIdNew
|
| 923 |
+
let stxNew := stx.setArg declIdx declNew
|
| 924 |
+
withMacroExpansion stx stxNew <| elabTerm stxNew expectedType?
|
| 925 |
+
else
|
| 926 |
+
throwUnsupportedSyntax
|
| 927 |
+
|
| 928 |
+
@[builtin_term_elab Β«letΒ»] def elabLetDecl : TermElab :=
|
| 929 |
+
fun stx expectedType? => elabLetDeclCore stx expectedType? {}
|
| 930 |
+
|
| 931 |
+
@[builtin_term_elab Β«haveΒ»] def elabHaveDecl : TermElab :=
|
| 932 |
+
fun stx expectedType? => elabLetDeclCore stx expectedType? { nondep := true }
|
| 933 |
+
|
| 934 |
+
@[builtin_term_elab Β«let_funΒ»] def elabLetFunDecl : TermElab :=
|
| 935 |
+
fun stx expectedType? => do
|
| 936 |
+
withRef stx <| Linter.logLintIf Linter.linter.deprecated stx[0]
|
| 937 |
+
"`let_fun` has been deprecated in favor of `have`"
|
| 938 |
+
elabLetDeclCore stx expectedType? { nondep := true }
|
| 939 |
+
|
| 940 |
+
@[builtin_term_elab Β«let_delayedΒ»] def elabLetDelayedDecl : TermElab :=
|
| 941 |
+
fun stx expectedType? => elabLetDeclCore stx expectedType? { postponeValue := true }
|
| 942 |
+
|
| 943 |
+
@[builtin_term_elab Β«let_tmpΒ»] def elabLetTmpDecl : TermElab :=
|
| 944 |
+
fun stx expectedType? => elabLetDeclCore stx expectedType? { usedOnly := true }
|
| 945 |
+
|
| 946 |
+
@[builtin_term_elab Β«letIΒ»] def elabLetIDecl : TermElab :=
|
| 947 |
+
fun stx expectedType? => elabLetDeclCore stx expectedType? { zeta := true }
|
| 948 |
+
|
| 949 |
+
@[builtin_term_elab Β«haveIΒ»] def elabHaveIDecl : TermElab :=
|
| 950 |
+
fun stx expectedType? => elabLetDeclCore stx expectedType? { zeta := true, nondep := true }
|
| 951 |
+
|
| 952 |
+
builtin_initialize
|
| 953 |
+
registerTraceClass `Elab.let
|
| 954 |
+
registerTraceClass `Elab.let.decl
|
| 955 |
+
registerTraceClass `Elab.autoParam
|
| 956 |
+
|
| 957 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BindersUtil.lean
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Parser.Term
|
| 8 |
+
|
| 9 |
+
namespace Lean.Elab.Term
|
| 10 |
+
/--
|
| 11 |
+
Recall that
|
| 12 |
+
```
|
| 13 |
+
def typeSpec := leading_parser " : " >> termParser
|
| 14 |
+
def optType : Parser := optional typeSpec
|
| 15 |
+
```
|
| 16 |
+
-/
|
| 17 |
+
def expandOptType (ref : Syntax) (optType : Syntax) : Syntax :=
|
| 18 |
+
if optType.isNone then
|
| 19 |
+
mkHole ref
|
| 20 |
+
else
|
| 21 |
+
optType[0][1]
|
| 22 |
+
|
| 23 |
+
open Lean.Parser.Term
|
| 24 |
+
|
| 25 |
+
/-- Helper function for `expandEqnsIntoMatch` -/
|
| 26 |
+
def getMatchAltsNumPatterns (matchAlts : Syntax) : Nat :=
|
| 27 |
+
let alt0 := matchAlts[0][0]
|
| 28 |
+
let pats := alt0[1][0].getSepArgs
|
| 29 |
+
pats.size
|
| 30 |
+
|
| 31 |
+
/--
|
| 32 |
+
Expand a match alternative such as `| 0 | 1 => rhs` to an array containing `| 0 => rhs` and `| 1 => rhs`.
|
| 33 |
+
-/
|
| 34 |
+
def expandMatchAlt (stx : TSyntax ``matchAlt) : MacroM (Array (TSyntax ``matchAlt)) :=
|
| 35 |
+
match stx with
|
| 36 |
+
| `(matchAltExpr| | $[$patss,*]|* => $rhs) =>
|
| 37 |
+
if patss.size β€ 1 then
|
| 38 |
+
return #[stx]
|
| 39 |
+
else
|
| 40 |
+
patss.mapM fun pats => `(matchAltExpr| | $pats,* => $rhs)
|
| 41 |
+
| _ => return #[stx]
|
| 42 |
+
|
| 43 |
+
def shouldExpandMatchAlt : TSyntax ``matchAlt β Bool
|
| 44 |
+
| `(matchAltExpr| | $[$patss,*]|* => $_) => patss.size > 1
|
| 45 |
+
| _ => false
|
| 46 |
+
|
| 47 |
+
def expandMatchAlts? (stx : Syntax) : MacroM (Option Syntax) := do
|
| 48 |
+
match stx with
|
| 49 |
+
| `(match $[$gen]? $[$motive]? $discrs,* with $alts:matchAlt*) =>
|
| 50 |
+
if alts.any shouldExpandMatchAlt then
|
| 51 |
+
let alts β alts.foldlM (init := #[]) fun alts alt => return alts ++ (β expandMatchAlt alt)
|
| 52 |
+
`(match $[$gen]? $[$motive]? $discrs,* with $alts:matchAlt*)
|
| 53 |
+
else
|
| 54 |
+
return none
|
| 55 |
+
| _ => return none
|
| 56 |
+
|
| 57 |
+
open TSyntax.Compat in
|
| 58 |
+
def clearInMatchAlt (stx : TSyntax ``matchAlt) (vars : Array Ident) : TSyntax ``matchAlt :=
|
| 59 |
+
stx.1.modifyArg 3 fun rhs => Unhygienic.run do
|
| 60 |
+
let mut rhs := rhs
|
| 61 |
+
for v in vars do
|
| 62 |
+
rhs β `(clear% $v; $rhs)
|
| 63 |
+
return rhs
|
| 64 |
+
|
| 65 |
+
def clearInMatch (stx : Syntax) (vars : Array Ident) : MacroM Syntax := do
|
| 66 |
+
if vars.isEmpty then return stx
|
| 67 |
+
match stx with
|
| 68 |
+
| `(match $[$gen]? $[$motive]? $discrs,* with $alts:matchAlt*) =>
|
| 69 |
+
let alts := alts.map (clearInMatchAlt Β· vars)
|
| 70 |
+
`(match $[$gen]? $[$motive]? $discrs,* with $alts:matchAlt*)
|
| 71 |
+
| _ => return stx
|
| 72 |
+
|
| 73 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinCommand.lean
ADDED
|
@@ -0,0 +1,676 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Util.CollectLevelParams
|
| 8 |
+
import Lean.Util.CollectAxioms
|
| 9 |
+
import Lean.Meta.Reduce
|
| 10 |
+
import Lean.Elab.DeclarationRange
|
| 11 |
+
import Lean.Elab.Eval
|
| 12 |
+
import Lean.Elab.Command
|
| 13 |
+
import Lean.Elab.Open
|
| 14 |
+
import Lean.Elab.SetOption
|
| 15 |
+
import Init.System.Platform
|
| 16 |
+
import Lean.Meta.Hint
|
| 17 |
+
|
| 18 |
+
namespace Lean.Elab.Command
|
| 19 |
+
|
| 20 |
+
@[builtin_command_elab moduleDoc] def elabModuleDoc : CommandElab := fun stx => do
|
| 21 |
+
match stx[1] with
|
| 22 |
+
| Syntax.atom _ val =>
|
| 23 |
+
let doc := val.extract 0 (val.endPos - β¨2β©)
|
| 24 |
+
let some range β Elab.getDeclarationRange? stx
|
| 25 |
+
| return -- must be from partial syntax, ignore
|
| 26 |
+
modifyEnv fun env => addMainModuleDoc env β¨doc, rangeβ©
|
| 27 |
+
| _ => throwErrorAt stx "unexpected module doc string{indentD stx[1]}"
|
| 28 |
+
|
| 29 |
+
private def addScope (isNewNamespace : Bool) (header : String) (newNamespace : Name)
|
| 30 |
+
(isNoncomputable isPublic : Bool := false) (attrs : List (TSyntax ``Parser.Term.attrInstance) := []) :
|
| 31 |
+
CommandElabM Unit := do
|
| 32 |
+
modify fun s => { s with
|
| 33 |
+
env := s.env.registerNamespace newNamespace,
|
| 34 |
+
scopes := { s.scopes.head! with
|
| 35 |
+
header := header, currNamespace := newNamespace
|
| 36 |
+
isNoncomputable := s.scopes.head!.isNoncomputable || isNoncomputable
|
| 37 |
+
isPublic := s.scopes.head!.isPublic || isPublic
|
| 38 |
+
attrs := s.scopes.head!.attrs ++ attrs
|
| 39 |
+
} :: s.scopes
|
| 40 |
+
}
|
| 41 |
+
pushScope
|
| 42 |
+
if isNewNamespace then
|
| 43 |
+
activateScoped newNamespace
|
| 44 |
+
|
| 45 |
+
private def addScopes (header : Name) (isNewNamespace : Bool) (isNoncomputable isPublic : Bool := false)
|
| 46 |
+
(attrs : List (TSyntax ``Parser.Term.attrInstance) := []) : CommandElabM Unit :=
|
| 47 |
+
go header
|
| 48 |
+
where go
|
| 49 |
+
| .anonymous => pure ()
|
| 50 |
+
| .str p header => do
|
| 51 |
+
go p
|
| 52 |
+
let currNamespace β getCurrNamespace
|
| 53 |
+
addScope isNewNamespace header (if isNewNamespace then Name.mkStr currNamespace header else currNamespace) isNoncomputable isPublic attrs
|
| 54 |
+
| _ => throwError "invalid scope"
|
| 55 |
+
|
| 56 |
+
private def addNamespace (header : Name) : CommandElabM Unit :=
|
| 57 |
+
addScopes (isNewNamespace := true) (isNoncomputable := false) (attrs := []) header
|
| 58 |
+
|
| 59 |
+
def withNamespace {Ξ±} (ns : Name) (elabFn : CommandElabM Ξ±) : CommandElabM Ξ± := do
|
| 60 |
+
addNamespace ns
|
| 61 |
+
let a β elabFn
|
| 62 |
+
modify fun s => { s with scopes := s.scopes.drop ns.getNumParts }
|
| 63 |
+
pure a
|
| 64 |
+
|
| 65 |
+
private def popScopes (numScopes : Nat) : CommandElabM Unit :=
|
| 66 |
+
for _ in [0:numScopes] do
|
| 67 |
+
popScope
|
| 68 |
+
|
| 69 |
+
private def innermostScopeName? : List Scope β Option Name
|
| 70 |
+
| { header := "", .. } :: _ => none
|
| 71 |
+
| { header := h, .. } :: _ => some <| .mkSimple h
|
| 72 |
+
| _ => some .anonymous -- should not happen
|
| 73 |
+
|
| 74 |
+
private def checkEndHeader : Name β List Scope β Option Name
|
| 75 |
+
| .anonymous, _ => none
|
| 76 |
+
| .str p s, { header := h, .. } :: scopes =>
|
| 77 |
+
if h == s then
|
| 78 |
+
(.str Β· s) <$> checkEndHeader p scopes
|
| 79 |
+
else
|
| 80 |
+
some <| .mkSimple h
|
| 81 |
+
| _, _ => some .anonymous -- should not happen
|
| 82 |
+
|
| 83 |
+
@[builtin_command_elab Β«namespaceΒ»] def elabNamespace : CommandElab := fun stx =>
|
| 84 |
+
match stx with
|
| 85 |
+
| `(namespace $n) => addNamespace n.getId
|
| 86 |
+
| _ => throwUnsupportedSyntax
|
| 87 |
+
|
| 88 |
+
@[builtin_command_elab Β«sectionΒ»] def elabSection : CommandElab := fun stx => do
|
| 89 |
+
match stx with
|
| 90 |
+
| `(Parser.Command.section| $[public%$publicTk]? $[@[expose%$expTk]]? $[noncomputable%$ncTk]? section $(header?)?) =>
|
| 91 |
+
-- TODO: allow more attributes?
|
| 92 |
+
let attrs β if expTk.isSome then
|
| 93 |
+
pure [β `(Parser.Term.attrInstance| expose)]
|
| 94 |
+
else
|
| 95 |
+
pure []
|
| 96 |
+
if let some header := header? then
|
| 97 |
+
addScopes (isNewNamespace := false) (isNoncomputable := ncTk.isSome) (isPublic := publicTk.isSome) (attrs := attrs) header.getId
|
| 98 |
+
else
|
| 99 |
+
addScope (isNewNamespace := false) (isNoncomputable := ncTk.isSome) (isPublic := publicTk.isSome) (attrs := attrs) "" (β getCurrNamespace)
|
| 100 |
+
| _ => throwUnsupportedSyntax
|
| 101 |
+
|
| 102 |
+
/--
|
| 103 |
+
Produces a `Name` composed of the names of at most the innermost `n` scopes in `ss`, truncating if an
|
| 104 |
+
empty scope is reached (so that we do not suggest names like `Foo.«».Bar`).
|
| 105 |
+
|
| 106 |
+
If `n` is not specified, will use all scopes in `ss`.
|
| 107 |
+
-/
|
| 108 |
+
private def nameOfScopes : (ss : List Scope) β (n : Nat := ss.length) β Name
|
| 109 |
+
| _, 0 => .anonymous
|
| 110 |
+
| [], _ => .anonymous
|
| 111 |
+
| s :: ss, n + 1 =>
|
| 112 |
+
if s.header == "" then
|
| 113 |
+
.anonymous
|
| 114 |
+
else
|
| 115 |
+
.str (nameOfScopes ss n) s.header
|
| 116 |
+
|
| 117 |
+
/--
|
| 118 |
+
Returns the first suffix of `base` that begins with `seg`, if one exists.
|
| 119 |
+
|
| 120 |
+
Note: this uses a naive, O(m*n) implementation for simplicity; we assume repeated partial overlap of
|
| 121 |
+
name components to be relatively uncommon, so that practical performance is closer to linear.
|
| 122 |
+
-/
|
| 123 |
+
private def findSuffixWithPrefix (base : Name) (seg : Name) : Option Name :=
|
| 124 |
+
findSuffixMatch base seg true
|
| 125 |
+
where
|
| 126 |
+
/--
|
| 127 |
+
Helper for `findSuffixWithPrefix`. If `allowOffset` is `false`, then `seg` must be a suffix of
|
| 128 |
+
`base`, not just a prefix of a suffix.
|
| 129 |
+
-/
|
| 130 |
+
findSuffixMatch : (base : Name) β (seg : Name) β (allowOffset : Bool) β Option Name
|
| 131 |
+
| _, .anonymous, _ => some .anonymous
|
| 132 |
+
| .anonymous, _, _ => none
|
| 133 |
+
| .num p n, seg@(.num p' n'), allowOffset => do
|
| 134 |
+
if n == n' then
|
| 135 |
+
if let some nm := findSuffixMatch p p' (allowOffset := false) then
|
| 136 |
+
return .num nm n
|
| 137 |
+
if allowOffset then
|
| 138 |
+
return .num (β findSuffixMatch p seg allowOffset) n
|
| 139 |
+
else
|
| 140 |
+
none
|
| 141 |
+
| .str p s, seg@(.str p' s'), allowOffset => do
|
| 142 |
+
if s == s' then
|
| 143 |
+
if let some nm := findSuffixMatch p p' (allowOffset := false) then
|
| 144 |
+
return .str nm s
|
| 145 |
+
if allowOffset then
|
| 146 |
+
return .str (β findSuffixMatch p seg allowOffset) s
|
| 147 |
+
else
|
| 148 |
+
none
|
| 149 |
+
| .str p s, seg, allowOffset =>
|
| 150 |
+
if allowOffset then
|
| 151 |
+
return .str (β findSuffixMatch p seg allowOffset) s
|
| 152 |
+
else
|
| 153 |
+
none
|
| 154 |
+
| .num p n, seg, allowOffset =>
|
| 155 |
+
if allowOffset then
|
| 156 |
+
return .num (β findSuffixMatch p seg allowOffset) n
|
| 157 |
+
else
|
| 158 |
+
none
|
| 159 |
+
|
| 160 |
+
private def throwNoScope : CommandElabM Unit :=
|
| 161 |
+
throwError m!"Invalid `end`: There is no current scope to end"
|
| 162 |
+
++ .note m!"Scopes are introduced using `namespace` and `section`"
|
| 163 |
+
|
| 164 |
+
private def throwMissingName (name : Name) : CommandElabM Unit := do
|
| 165 |
+
let hint β liftCoreM <| MessageData.hint m!"To end the current scope `{name}`, specify its name:"
|
| 166 |
+
#[β `(end $(mkIdent name))] (codeActionPrefix? := "Add scope name: ")
|
| 167 |
+
throwError "Missing name after `end`: Expected the current scope name `{name}`{hint}"
|
| 168 |
+
|
| 169 |
+
/--
|
| 170 |
+
Produces a hint message with a suggestion to replace the name following `end` at the current ref
|
| 171 |
+
with the name given by `scopes` if there is only one active scope; otherwise, returns `none`.
|
| 172 |
+
|
| 173 |
+
Rationale: When there is only one active scope, only one valid `end` command is possible, so we
|
| 174 |
+
suggest it; if there are multiple, then it is harder to determine with confidence which the user
|
| 175 |
+
intended to end.
|
| 176 |
+
-/
|
| 177 |
+
private def mkSingleScopeReplacementHint? (scopes : List Scope) := do
|
| 178 |
+
-- Recall that there is always an anonymous topmost scope, so `scopes.length = 2` when there is
|
| 179 |
+
-- only one active scope:
|
| 180 |
+
if scopes.length == 2 then
|
| 181 |
+
let name := nameOfScopes scopes
|
| 182 |
+
some <$> MessageData.hint m!"Use current scope name `{name}`:" #[(β `(end $(mkIdent name)))]
|
| 183 |
+
(codeActionPrefix? := "Replace scope name: ")
|
| 184 |
+
else
|
| 185 |
+
return none
|
| 186 |
+
|
| 187 |
+
private def throwTooManyScopeComponents (header : Name) (scopes : List Scope) : CommandElabM Unit := do
|
| 188 |
+
let hint β liftCoreM do
|
| 189 |
+
if let some hint β mkSingleScopeReplacementHint? scopes then
|
| 190 |
+
pure hint
|
| 191 |
+
else
|
| 192 |
+
let scopesName := nameOfScopes scopes
|
| 193 |
+
pure <| MessageData.hint' m!"The name after `end` must be `{scopesName}` or some suffix thereof"
|
| 194 |
+
throwError m!"Invalid name after `end`: `{header}` contains too many components" ++ hint
|
| 195 |
+
|
| 196 |
+
private def throwScopeNameMismatch (header : Name) (scopes : List Scope) (endSize : Nat)
|
| 197 |
+
: CommandElabM Unit := do
|
| 198 |
+
let correspondingScopes := nameOfScopes scopes endSize
|
| 199 |
+
let allScopes := nameOfScopes scopes
|
| 200 |
+
let help β liftCoreM do
|
| 201 |
+
if let some hint β mkSingleScopeReplacementHint? scopes then
|
| 202 |
+
pure hint
|
| 203 |
+
else if let some suffix := findSuffixWithPrefix allScopes header then
|
| 204 |
+
let hintMsg := m!"If you meant to end the outer scope(s) `{header}`, you must end all the \
|
| 205 |
+
intervening scopes `{suffix}`:"
|
| 206 |
+
MessageData.hint hintMsg #[β `(end $(mkIdent suffix))]
|
| 207 |
+
(codeActionPrefix? := "Add intervening scopes: ")
|
| 208 |
+
else if correspondingScopes != allScopes then
|
| 209 |
+
pure <| .note m!"The current scopes are `{allScopes}`"
|
| 210 |
+
else pure .nil
|
| 211 |
+
throwError m!"Invalid name after `end`: Expected `{correspondingScopes}`, but found `{header}`" ++ help
|
| 212 |
+
|
| 213 |
+
private def throwUnnecessaryScopeName (header : Name) : CommandElabM Unit := do
|
| 214 |
+
let hintMsg := m!"Delete the name `{header}` to end the current unnamed scope; outer named scopes \
|
| 215 |
+
can then be closed using additional `end` command(s):"
|
| 216 |
+
let hint β liftCoreM <| MessageData.hint hintMsg #[β `(end)] (codeActionPrefix? := "Delete name: ")
|
| 217 |
+
throwError m!"Unexpected name `{header}` after `end`: The current section is unnamed" ++ hint
|
| 218 |
+
|
| 219 |
+
@[builtin_command_elab Β«endΒ»] def elabEnd : CommandElab := fun stx => do
|
| 220 |
+
let header? := (stx.getArg 1).getOptionalIdent?
|
| 221 |
+
let endSize : Nat := match header? with
|
| 222 |
+
| none => 1
|
| 223 |
+
| some n => n.getNumParts
|
| 224 |
+
let scopes β getScopes
|
| 225 |
+
let numScopes := scopes.length
|
| 226 |
+
if numScopes == 1 then
|
| 227 |
+
throwNoScope
|
| 228 |
+
match header? with
|
| 229 |
+
| none =>
|
| 230 |
+
if let some name := innermostScopeName? scopes then
|
| 231 |
+
throwMissingName name
|
| 232 |
+
| some header =>
|
| 233 |
+
if endSize >= numScopes then
|
| 234 |
+
throwTooManyScopeComponents header scopes
|
| 235 |
+
else
|
| 236 |
+
let scopesName := nameOfScopes scopes endSize
|
| 237 |
+
if scopesName != header then
|
| 238 |
+
if scopesName == .anonymous then
|
| 239 |
+
throwUnnecessaryScopeName header
|
| 240 |
+
else
|
| 241 |
+
throwScopeNameMismatch header scopes endSize
|
| 242 |
+
modify fun s => {s with scopes := s.scopes.drop endSize }
|
| 243 |
+
popScopes endSize
|
| 244 |
+
|
| 245 |
+
private partial def elabChoiceAux (cmds : Array Syntax) (i : Nat) : CommandElabM Unit :=
|
| 246 |
+
if h : i < cmds.size then
|
| 247 |
+
catchInternalId unsupportedSyntaxExceptionId
|
| 248 |
+
(elabCommand cmds[i])
|
| 249 |
+
(fun _ => elabChoiceAux cmds (i+1))
|
| 250 |
+
else
|
| 251 |
+
throwUnsupportedSyntax
|
| 252 |
+
|
| 253 |
+
@[builtin_command_elab choice] def elabChoice : CommandElab := fun stx =>
|
| 254 |
+
elabChoiceAux stx.getArgs 0
|
| 255 |
+
|
| 256 |
+
@[builtin_command_elab Β«universeΒ»] def elabUniverse : CommandElab := fun n => do
|
| 257 |
+
n[1].forArgsM addUnivLevel
|
| 258 |
+
|
| 259 |
+
@[builtin_command_elab Β«init_quotΒ»] def elabInitQuot : CommandElab := fun _ => do
|
| 260 |
+
liftCoreM <| addDecl Declaration.quotDecl
|
| 261 |
+
|
| 262 |
+
@[builtin_command_elab Β«exportΒ»] def elabExport : CommandElab := fun stx => do
|
| 263 |
+
let `(export $ns ($ids*)) := stx | throwUnsupportedSyntax
|
| 264 |
+
let nss β resolveNamespace ns
|
| 265 |
+
let currNamespace β getCurrNamespace
|
| 266 |
+
if nss == [currNamespace] then throwError "invalid 'export', self export"
|
| 267 |
+
let mut aliases := #[]
|
| 268 |
+
for idStx in ids do
|
| 269 |
+
let id := idStx.getId
|
| 270 |
+
let declName β resolveNameUsingNamespaces nss idStx
|
| 271 |
+
if (β getInfoState).enabled then
|
| 272 |
+
addConstInfo idStx declName
|
| 273 |
+
aliases := aliases.push (currNamespace ++ id, declName)
|
| 274 |
+
modify fun s => { s with env := aliases.foldl (init := s.env) fun env p => addAlias env p.1 p.2 }
|
| 275 |
+
|
| 276 |
+
@[builtin_command_elab Β«openΒ»] def elabOpen : CommandElab
|
| 277 |
+
| `(open $decl:openDecl) => do
|
| 278 |
+
let openDecls β elabOpenDecl decl
|
| 279 |
+
modifyScope fun scope => { scope with openDecls := openDecls }
|
| 280 |
+
| _ => throwUnsupportedSyntax
|
| 281 |
+
|
| 282 |
+
open Lean.Parser.Term
|
| 283 |
+
|
| 284 |
+
private def typelessBinder? : Syntax β Option (Array (TSyntax [`ident, `Lean.Parser.Term.hole]) Γ BinderInfo)
|
| 285 |
+
| `(bracketedBinderF|($ids*)) => some (ids, .default)
|
| 286 |
+
| `(bracketedBinderF|{$ids*}) => some (ids, .implicit)
|
| 287 |
+
| `(bracketedBinderF|β¦$ids*β¦) => some (ids, .strictImplicit)
|
| 288 |
+
| `(bracketedBinderF|[$id:ident]) => some (#[id], .instImplicit)
|
| 289 |
+
| _ => none
|
| 290 |
+
|
| 291 |
+
/-- If `id` is an identifier, return true if `ids` contains `id`. -/
|
| 292 |
+
private def containsId (ids : Array (TSyntax [`ident, ``Parser.Term.hole])) (id : TSyntax [`ident, ``Parser.Term.hole]) : Bool :=
|
| 293 |
+
id.raw.isIdent && ids.any fun id' => id'.raw.getId == id.raw.getId
|
| 294 |
+
|
| 295 |
+
/--
|
| 296 |
+
Auxiliary method for processing binder annotation update commands:
|
| 297 |
+
`variable (Ξ±)`, `variable {Ξ±}`, `variable β¦Ξ±β¦`, and `variable [Ξ±]`.
|
| 298 |
+
The argument `binder` is the binder of the `variable` command.
|
| 299 |
+
The method returns an array containing the "residue", that is, variables that do not correspond to updates.
|
| 300 |
+
Recall that a `bracketedBinder` can be of the form `(x y)`.
|
| 301 |
+
```
|
| 302 |
+
variable {Ξ± Ξ² : Type}
|
| 303 |
+
variable (Ξ± Ξ³)
|
| 304 |
+
```
|
| 305 |
+
The second `variable` command updates the binder annotation for `Ξ±`, and returns "residue" `Ξ³`.
|
| 306 |
+
-/
|
| 307 |
+
private def replaceBinderAnnotation (binder : TSyntax ``Parser.Term.bracketedBinder) : CommandElabM (Array (TSyntax ``Parser.Term.bracketedBinder)) := do
|
| 308 |
+
let some (binderIds, binderInfo) := typelessBinder? binder | return #[binder]
|
| 309 |
+
let varDecls := (β getScope).varDecls
|
| 310 |
+
let mut varDeclsNew := #[]
|
| 311 |
+
let mut binderIds := binderIds
|
| 312 |
+
let mut binderIdsIniSize := binderIds.size
|
| 313 |
+
let mut modifiedVarDecls := false
|
| 314 |
+
-- Go through declarations in reverse to respect shadowing
|
| 315 |
+
for varDecl in varDecls.reverse do
|
| 316 |
+
let (ids, ty?, binderInfo') β match varDecl with
|
| 317 |
+
| `(bracketedBinderF|($ids* $[: $ty?]? $(annot?)?)) =>
|
| 318 |
+
if annot?.isSome then
|
| 319 |
+
for binderId in binderIds do
|
| 320 |
+
if containsId ids binderId then
|
| 321 |
+
throwErrorAt binderId "cannot update binder annotation of variables with default values/tactics"
|
| 322 |
+
pure (ids, ty?, .default)
|
| 323 |
+
| `(bracketedBinderF|{$ids* $[: $ty?]?}) =>
|
| 324 |
+
pure (ids, ty?, .implicit)
|
| 325 |
+
| `(bracketedBinderF|β¦$ids* $[: $ty?]?β¦) =>
|
| 326 |
+
pure (ids, ty?, .strictImplicit)
|
| 327 |
+
| `(bracketedBinderF|[$id : $ty]) =>
|
| 328 |
+
pure (#[β¨idβ©], some ty, .instImplicit)
|
| 329 |
+
| _ =>
|
| 330 |
+
varDeclsNew := varDeclsNew.push varDecl; continue
|
| 331 |
+
if binderInfo == binderInfo' then
|
| 332 |
+
-- no update, ensure we don't have redundant annotations.
|
| 333 |
+
for binderId in binderIds do
|
| 334 |
+
if containsId ids binderId then
|
| 335 |
+
throwErrorAt binderId "redundant binder annotation update"
|
| 336 |
+
varDeclsNew := varDeclsNew.push varDecl
|
| 337 |
+
else if binderIds.all fun binderId => !containsId ids binderId then
|
| 338 |
+
-- `binderIds` and `ids` are disjoint
|
| 339 |
+
varDeclsNew := varDeclsNew.push varDecl
|
| 340 |
+
else
|
| 341 |
+
let mkBinder (id : TSyntax [`ident, ``Parser.Term.hole]) (binderInfo : BinderInfo) : CommandElabM (TSyntax ``Parser.Term.bracketedBinder) :=
|
| 342 |
+
match binderInfo with
|
| 343 |
+
| .default => `(bracketedBinderF| ($id $[: $ty?]?))
|
| 344 |
+
| .implicit => `(bracketedBinderF| {$id $[: $ty?]?})
|
| 345 |
+
| .strictImplicit => `(bracketedBinderF| {{$id $[: $ty?]?}})
|
| 346 |
+
| .instImplicit => do
|
| 347 |
+
let some ty := ty?
|
| 348 |
+
| throwErrorAt binder "cannot update binder annotation of variable '{id}' to instance implicit:\n\
|
| 349 |
+
variable was originally declared without an explicit type"
|
| 350 |
+
`(bracketedBinderF| [$(β¨idβ©) : $ty])
|
| 351 |
+
for id in ids.reverse do
|
| 352 |
+
if let some idx := binderIds.findFinIdx? fun binderId => binderId.raw.isIdent && binderId.raw.getId == id.raw.getId then
|
| 353 |
+
binderIds := binderIds.eraseIdx idx
|
| 354 |
+
modifiedVarDecls := true
|
| 355 |
+
let newBinder β mkBinder id binderInfo
|
| 356 |
+
if binderInfo.isInstImplicit then
|
| 357 |
+
-- We elaborate the new binder to make sure it's valid as instance implicit
|
| 358 |
+
try
|
| 359 |
+
runTermElabM fun _ => Term.withSynthesize <| Term.withAutoBoundImplicit <|
|
| 360 |
+
Term.elabBinder newBinder fun _ => pure ()
|
| 361 |
+
catch e =>
|
| 362 |
+
throwErrorAt binder m!"cannot update binder annotation of variable '{id}' to instance implicit:\n\
|
| 363 |
+
{e.toMessageData}"
|
| 364 |
+
varDeclsNew := varDeclsNew.push (β mkBinder id binderInfo)
|
| 365 |
+
else
|
| 366 |
+
varDeclsNew := varDeclsNew.push (β mkBinder id binderInfo')
|
| 367 |
+
if modifiedVarDecls then
|
| 368 |
+
modifyScope fun scope => { scope with varDecls := varDeclsNew.reverse }
|
| 369 |
+
if binderIds.size != binderIdsIniSize then
|
| 370 |
+
binderIds.mapM fun binderId =>
|
| 371 |
+
match binderInfo with
|
| 372 |
+
| .default => `(bracketedBinderF| ($binderId))
|
| 373 |
+
| .implicit => `(bracketedBinderF| {$binderId})
|
| 374 |
+
| .strictImplicit => `(bracketedBinderF| {{$binderId}})
|
| 375 |
+
| .instImplicit => throwUnsupportedSyntax
|
| 376 |
+
else
|
| 377 |
+
return #[binder]
|
| 378 |
+
|
| 379 |
+
@[builtin_command_elab Β«variableΒ»] def elabVariable : CommandElab
|
| 380 |
+
| `(variable%$tk $binders*) => do
|
| 381 |
+
let binders β binders.flatMapM replaceBinderAnnotation
|
| 382 |
+
-- Try to elaborate `binders` for sanity checking
|
| 383 |
+
runTermElabM fun _ => Term.withSynthesize <| Term.withAutoBoundImplicit <|
|
| 384 |
+
Term.elabBinders binders fun xs => do
|
| 385 |
+
-- Determine the set of auto-implicits for this variable command and add an inlay hint
|
| 386 |
+
-- for them. We will only actually add the auto-implicits to a type when the variables
|
| 387 |
+
-- declared here are used in some other declaration, but this is nonetheless the right
|
| 388 |
+
-- place to display the inlay hint.
|
| 389 |
+
let _ β Term.addAutoBoundImplicits xs (tk.getTailPos? (canonicalOnly := true))
|
| 390 |
+
-- Remark: if we want to produce error messages when variables shadow existing ones, here is the place to do it.
|
| 391 |
+
for binder in binders do
|
| 392 |
+
let varUIds β (β getBracketedBinderIds binder) |>.mapM (withFreshMacroScope β MonadQuotation.addMacroScope)
|
| 393 |
+
modifyScope fun scope => { scope with varDecls := scope.varDecls.push binder, varUIds := scope.varUIds ++ varUIds }
|
| 394 |
+
| _ => throwUnsupportedSyntax
|
| 395 |
+
|
| 396 |
+
open Meta
|
| 397 |
+
|
| 398 |
+
def elabCheckCore (ignoreStuckTC : Bool) : CommandElab
|
| 399 |
+
| `(#check%$tk $term) => withoutModifyingEnv <| runTermElabM fun _ => Term.withDeclName `_check do
|
| 400 |
+
-- show signature for `#check id`/`#check @id`
|
| 401 |
+
if let `($id:ident) := term then
|
| 402 |
+
try
|
| 403 |
+
for c in (β realizeGlobalConstWithInfos term) do
|
| 404 |
+
addCompletionInfo <| .id term id.getId (danglingDot := false) {} none
|
| 405 |
+
logInfoAt tk <| .signature c
|
| 406 |
+
return
|
| 407 |
+
catch _ => pure () -- identifier might not be a constant but constant + projection
|
| 408 |
+
let e β Term.elabTerm term none
|
| 409 |
+
Term.synthesizeSyntheticMVarsNoPostponing (ignoreStuckTC := ignoreStuckTC)
|
| 410 |
+
-- Users might be testing out buggy elaborators. Let's typecheck before proceeding:
|
| 411 |
+
withRef tk <| Meta.check e
|
| 412 |
+
let e β Term.levelMVarToParam (β instantiateMVars e)
|
| 413 |
+
if e.isSyntheticSorry then
|
| 414 |
+
return
|
| 415 |
+
let type β inferType e
|
| 416 |
+
logInfoAt tk m!"{e} : {type}"
|
| 417 |
+
| _ => throwUnsupportedSyntax
|
| 418 |
+
|
| 419 |
+
@[builtin_command_elab Lean.Parser.Command.check] def elabCheck : CommandElab := elabCheckCore (ignoreStuckTC := true)
|
| 420 |
+
|
| 421 |
+
@[builtin_command_elab Lean.reduceCmd] def elabReduce : CommandElab
|
| 422 |
+
| `(#reduce%$tk $term) => go tk term
|
| 423 |
+
| `(#reduce%$tk (proofs := true) $term) => go tk term (skipProofs := false)
|
| 424 |
+
| `(#reduce%$tk (types := true) $term) => go tk term (skipTypes := false)
|
| 425 |
+
| `(#reduce%$tk (proofs := true) (types := true) $term) => go tk term (skipProofs := false) (skipTypes := false)
|
| 426 |
+
| _ => throwUnsupportedSyntax
|
| 427 |
+
where
|
| 428 |
+
go (tk : Syntax) (term : Syntax) (skipProofs := true) (skipTypes := true) : CommandElabM Unit :=
|
| 429 |
+
withoutModifyingEnv <| runTermElabM fun _ => Term.withDeclName `_reduce do
|
| 430 |
+
let e β Term.elabTerm term none
|
| 431 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 432 |
+
-- Users might be testing out buggy elaborators. Let's typecheck before proceeding:
|
| 433 |
+
withRef tk <| Meta.check e
|
| 434 |
+
let e β Term.levelMVarToParam (β instantiateMVars e)
|
| 435 |
+
-- TODO: add options or notation for setting the following parameters
|
| 436 |
+
withTheReader Core.Context (fun ctx => { ctx with options := ctx.options.setBool `smartUnfolding false }) do
|
| 437 |
+
let e β withTransparency (mode := TransparencyMode.all) <| reduce e (skipProofs := skipProofs) (skipTypes := skipTypes)
|
| 438 |
+
logInfoAt tk e
|
| 439 |
+
|
| 440 |
+
def hasNoErrorMessages : CommandElabM Bool := do
|
| 441 |
+
return !(β get).messages.hasErrors
|
| 442 |
+
|
| 443 |
+
def failIfSucceeds (x : CommandElabM Unit) : CommandElabM Unit := do
|
| 444 |
+
let resetMessages : CommandElabM MessageLog := do
|
| 445 |
+
let s β get
|
| 446 |
+
let messages := s.messages;
|
| 447 |
+
modify fun s => { s with messages := {} };
|
| 448 |
+
pure messages
|
| 449 |
+
let restoreMessages (prevMessages : MessageLog) : CommandElabM Unit := do
|
| 450 |
+
modify fun s => { s with messages := prevMessages ++ s.messages.errorsToInfos }
|
| 451 |
+
let prevMessages β resetMessages
|
| 452 |
+
let succeeded β try
|
| 453 |
+
x
|
| 454 |
+
hasNoErrorMessages
|
| 455 |
+
catch
|
| 456 |
+
| ex@(Exception.error _ _) => do logException ex; pure false
|
| 457 |
+
| Exception.internal id _ => do logError (β id.getName); pure false
|
| 458 |
+
finally
|
| 459 |
+
restoreMessages prevMessages
|
| 460 |
+
if succeeded then
|
| 461 |
+
throwError "unexpected success"
|
| 462 |
+
|
| 463 |
+
@[builtin_command_elab Β«check_failureΒ»] def elabCheckFailure : CommandElab
|
| 464 |
+
| `(#check_failure $term) => do
|
| 465 |
+
failIfSucceeds <| elabCheckCore (ignoreStuckTC := false) (β `(#check $term))
|
| 466 |
+
| _ => throwUnsupportedSyntax
|
| 467 |
+
|
| 468 |
+
@[builtin_command_elab Β«synthΒ»] def elabSynth : CommandElab := fun stx => do
|
| 469 |
+
let term := stx[1]
|
| 470 |
+
withoutModifyingEnv <| runTermElabM fun _ => Term.withDeclName `_synth_cmd do
|
| 471 |
+
let inst β Term.elabTerm term none
|
| 472 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 473 |
+
let inst β instantiateMVars inst
|
| 474 |
+
let val β synthInstance inst
|
| 475 |
+
logInfo val
|
| 476 |
+
pure ()
|
| 477 |
+
|
| 478 |
+
@[builtin_command_elab Β«set_optionΒ»] def elabSetOption : CommandElab := fun stx => do
|
| 479 |
+
let options β Elab.elabSetOption stx[1] stx[3]
|
| 480 |
+
modify fun s => { s with maxRecDepth := maxRecDepth.get options }
|
| 481 |
+
modifyScope fun scope => { scope with opts := options }
|
| 482 |
+
|
| 483 |
+
@[builtin_macro Lean.Parser.Command.Β«inΒ»] def expandInCmd : Macro
|
| 484 |
+
| `($cmdβ in%$tk $cmdβ) =>
|
| 485 |
+
-- Limit ref variability for incrementality; see Note [Incremental Macros]
|
| 486 |
+
withRef tk `(section $cmdβ:command $cmdβ end)
|
| 487 |
+
| _ => Macro.throwUnsupported
|
| 488 |
+
|
| 489 |
+
@[builtin_command_elab Parser.Command.addDocString] def elabAddDeclDoc : CommandElab := fun stx => do
|
| 490 |
+
match stx with
|
| 491 |
+
| `($doc:docComment add_decl_doc $id) =>
|
| 492 |
+
let declName β liftCoreM <| realizeGlobalConstNoOverloadWithInfo id
|
| 493 |
+
unless ((β getEnv).getModuleIdxFor? declName).isNone do
|
| 494 |
+
throwError "invalid 'add_decl_doc', declaration is in an imported module"
|
| 495 |
+
if let .none β findDeclarationRangesCore? declName then
|
| 496 |
+
-- this is only relevant for declarations added without a declaration range
|
| 497 |
+
-- in particular `Quot.mk` et al which are added by `init_quot`
|
| 498 |
+
addDeclarationRangesFromSyntax declName stx id
|
| 499 |
+
addDocString declName doc
|
| 500 |
+
| _ => throwUnsupportedSyntax
|
| 501 |
+
|
| 502 |
+
@[builtin_command_elab Lean.Parser.Command.include] def elabInclude : CommandElab
|
| 503 |
+
| `(Lean.Parser.Command.include| include $ids*) => do
|
| 504 |
+
let sc β getScope
|
| 505 |
+
let vars β sc.varDecls.flatMapM getBracketedBinderIds
|
| 506 |
+
let mut uids := #[]
|
| 507 |
+
for id in ids do
|
| 508 |
+
if let some idx := vars.findIdx? (Β· == id.getId) then
|
| 509 |
+
uids := uids.push sc.varUIds[idx]!
|
| 510 |
+
else
|
| 511 |
+
throwError "invalid 'include', variable '{id}' has not been declared in the current scope"
|
| 512 |
+
modifyScope fun sc => { sc with
|
| 513 |
+
includedVars := sc.includedVars ++ uids.toList
|
| 514 |
+
omittedVars := sc.omittedVars.filter (!uids.contains Β·) }
|
| 515 |
+
| _ => throwUnsupportedSyntax
|
| 516 |
+
|
| 517 |
+
@[builtin_command_elab Lean.Parser.Command.omit] def elabOmit : CommandElab
|
| 518 |
+
| `(Lean.Parser.Command.omit| omit $omits*) => do
|
| 519 |
+
-- TODO: this really shouldn't have to re-elaborate section vars... they should come
|
| 520 |
+
-- pre-elaborated
|
| 521 |
+
let omittedVars β runTermElabM fun vars => do
|
| 522 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 523 |
+
-- We don't want to store messages produced when elaborating `(getVarDecls s)` because they have already been saved when we elaborated the `variable`(s) command.
|
| 524 |
+
-- So, we use `Core.resetMessageLog`.
|
| 525 |
+
Core.resetMessageLog
|
| 526 |
+
-- resolve each omit to variable user name or type pattern
|
| 527 |
+
let elaboratedOmits : Array (Sum Name Expr) β omits.mapM fun
|
| 528 |
+
| `(ident| $id:ident) => pure <| Sum.inl id.getId
|
| 529 |
+
| `(Lean.Parser.Term.instBinder| [$id : $_]) => pure <| Sum.inl id.getId
|
| 530 |
+
| `(Lean.Parser.Term.instBinder| [$ty]) =>
|
| 531 |
+
Sum.inr <$> Term.withoutErrToSorry (Term.elabTermAndSynthesize ty none)
|
| 532 |
+
| _ => throwUnsupportedSyntax
|
| 533 |
+
-- check that each omit is actually used in the end
|
| 534 |
+
let mut omitsUsed := omits.map fun _ => false
|
| 535 |
+
let mut omittedVars := #[]
|
| 536 |
+
let mut revSectionFVars : Std.HashMap FVarId Name := {}
|
| 537 |
+
for (uid, var) in (β read).sectionFVars do
|
| 538 |
+
revSectionFVars := revSectionFVars.insert var.fvarId! uid
|
| 539 |
+
for var in vars do
|
| 540 |
+
let ldecl β var.fvarId!.getDecl
|
| 541 |
+
if let some idx := (β elaboratedOmits.findIdxM? fun
|
| 542 |
+
| .inl id => return ldecl.userName == id
|
| 543 |
+
| .inr ty => do
|
| 544 |
+
let mctx β getMCtx
|
| 545 |
+
isDefEq ty ldecl.type <* setMCtx mctx) then
|
| 546 |
+
if let some uid := revSectionFVars[var.fvarId!]? then
|
| 547 |
+
omittedVars := omittedVars.push uid
|
| 548 |
+
omitsUsed := omitsUsed.set! idx true
|
| 549 |
+
else
|
| 550 |
+
throwError "invalid 'omit', '{ldecl.userName}' has not been declared in the current scope"
|
| 551 |
+
for o in omits, used in omitsUsed do
|
| 552 |
+
unless used do
|
| 553 |
+
throwError "'{o}' did not match any variables in the current scope"
|
| 554 |
+
return omittedVars
|
| 555 |
+
modifyScope fun sc => { sc with
|
| 556 |
+
omittedVars := sc.omittedVars ++ omittedVars.toList
|
| 557 |
+
includedVars := sc.includedVars.filter (!omittedVars.contains Β·) }
|
| 558 |
+
| _ => throwUnsupportedSyntax
|
| 559 |
+
|
| 560 |
+
@[builtin_command_elab version] def elabVersion : CommandElab := fun _ => do
|
| 561 |
+
let mut target := System.Platform.target
|
| 562 |
+
if target.isEmpty then target := "unknown"
|
| 563 |
+
-- Only one should be set, but good to know if multiple are set in error.
|
| 564 |
+
let platforms :=
|
| 565 |
+
(if System.Platform.isWindows then [" Windows"] else [])
|
| 566 |
+
++ (if System.Platform.isOSX then [" macOS"] else [])
|
| 567 |
+
++ (if System.Platform.isEmscripten then [" Emscripten"] else [])
|
| 568 |
+
logInfo m!"Lean {Lean.versionString}\nTarget: {target}{String.join platforms}"
|
| 569 |
+
|
| 570 |
+
@[builtin_command_elab Parser.Command.exit] def elabExit : CommandElab := fun _ =>
|
| 571 |
+
logWarning "using 'exit' to interrupt Lean"
|
| 572 |
+
|
| 573 |
+
@[builtin_command_elab Parser.Command.import] def elabImport : CommandElab := fun _ =>
|
| 574 |
+
throwError "invalid 'import' command, it must be used in the beginning of the file"
|
| 575 |
+
|
| 576 |
+
@[builtin_command_elab Parser.Command.eoi] def elabEoi : CommandElab := fun _ =>
|
| 577 |
+
return
|
| 578 |
+
|
| 579 |
+
@[builtin_command_elab Parser.Command.where] def elabWhere : CommandElab := fun _ => do
|
| 580 |
+
let scope β getScope
|
| 581 |
+
let mut msg : Array MessageData := #[]
|
| 582 |
+
-- Noncomputable
|
| 583 |
+
if scope.isNoncomputable then
|
| 584 |
+
msg := msg.push <| β `(Parser.Command.section| noncomputable section)
|
| 585 |
+
-- Namespace
|
| 586 |
+
if !scope.currNamespace.isAnonymous then
|
| 587 |
+
msg := msg.push <| β `(command| namespace $(mkIdent scope.currNamespace))
|
| 588 |
+
-- Open namespaces
|
| 589 |
+
if let some openMsg β describeOpenDecls scope.openDecls.reverse then
|
| 590 |
+
msg := msg.push openMsg
|
| 591 |
+
-- Universe levels
|
| 592 |
+
if !scope.levelNames.isEmpty then
|
| 593 |
+
let levels := scope.levelNames.reverse.map mkIdent
|
| 594 |
+
msg := msg.push <| β `(command| universe $levels.toArray*)
|
| 595 |
+
-- Variables
|
| 596 |
+
if !scope.varDecls.isEmpty then
|
| 597 |
+
let varDecls : Array (TSyntax `Lean.Parser.Term.bracketedBinder) := scope.varDecls.map (β¨Β·.raw.unsetTrailingβ©)
|
| 598 |
+
msg := msg.push <| β `(command| variable $varDecls*)
|
| 599 |
+
-- Included variables
|
| 600 |
+
if !scope.includedVars.isEmpty then
|
| 601 |
+
msg := msg.push <| β `(command| include $(scope.includedVars.toArray.map (mkIdent Β·.eraseMacroScopes))*)
|
| 602 |
+
-- Options
|
| 603 |
+
if let some optionsMsg β describeOptions scope.opts then
|
| 604 |
+
msg := msg.push optionsMsg
|
| 605 |
+
if msg.isEmpty then
|
| 606 |
+
logInfo m!"-- In root namespace with initial scope"
|
| 607 |
+
else
|
| 608 |
+
logInfo <| MessageData.joinSep msg.toList "\n\n"
|
| 609 |
+
where
|
| 610 |
+
/--
|
| 611 |
+
'Delaborate' open declarations.
|
| 612 |
+
Current limitations:
|
| 613 |
+
- does not check whether or not successive namespaces need `_root_`
|
| 614 |
+
- does not combine commands with `renaming` clauses into a single command
|
| 615 |
+
-/
|
| 616 |
+
describeOpenDecls (ds : List OpenDecl) : CommandElabM (Option MessageData) := do
|
| 617 |
+
let mut lines : Array MessageData := #[]
|
| 618 |
+
let mut simple : Array Name := #[]
|
| 619 |
+
let flush (lines : Array MessageData) (simple : Array Name) : CommandElabM (Array MessageData Γ Array Name) := do
|
| 620 |
+
if simple.isEmpty then
|
| 621 |
+
return (lines, simple)
|
| 622 |
+
else
|
| 623 |
+
return (lines.push <| β `(command| open $(simple.map mkIdent)*), #[])
|
| 624 |
+
for d in ds do
|
| 625 |
+
match d with
|
| 626 |
+
| .explicit id decl =>
|
| 627 |
+
(lines, simple) β flush lines simple
|
| 628 |
+
let ns := decl.getPrefix
|
| 629 |
+
let Β«fromΒ» := Name.mkSimple decl.getString!
|
| 630 |
+
lines := lines.push <| β `(command| open $(mkIdent ns) renaming $(mkIdent Β«fromΒ») β $(mkIdent id))
|
| 631 |
+
| .simple ns ex =>
|
| 632 |
+
if ex == [] then
|
| 633 |
+
simple := simple.push ns
|
| 634 |
+
else
|
| 635 |
+
(lines, simple) β flush lines simple
|
| 636 |
+
lines := lines.push <| β `(command| open $(mkIdent ns) hiding $[$(ex.toArray.map mkIdent)]*)
|
| 637 |
+
(lines, _) β flush lines simple
|
| 638 |
+
return if lines.isEmpty then none else MessageData.joinSep lines.toList "\n"
|
| 639 |
+
|
| 640 |
+
describeOptions (opts : Options) : CommandElabM (Option MessageData) := do
|
| 641 |
+
let mut lines : Array MessageData := #[]
|
| 642 |
+
let decls β getOptionDecls
|
| 643 |
+
for (name, val) in opts do
|
| 644 |
+
-- `#guard_msgs` sets this option internally, we don't want it to end up in its output
|
| 645 |
+
if name == `Elab.async then
|
| 646 |
+
continue
|
| 647 |
+
let (isSet, isUnknown) :=
|
| 648 |
+
match decls.find? name with
|
| 649 |
+
| some decl => (decl.defValue != val, false)
|
| 650 |
+
| none => (true, true)
|
| 651 |
+
if isSet then
|
| 652 |
+
let cmd : TSyntax `command β
|
| 653 |
+
match val with
|
| 654 |
+
| .ofBool true => `(set_option $(mkIdent name) true)
|
| 655 |
+
| .ofBool false => `(set_option $(mkIdent name) false)
|
| 656 |
+
| .ofString str => `(set_option $(mkIdent name) $(Syntax.mkStrLit str))
|
| 657 |
+
| .ofNat n => `(set_option $(mkIdent name) $(Syntax.mkNatLit n))
|
| 658 |
+
| _ => `(set_option $(mkIdent name) 0 /- unrepresentable value -/)
|
| 659 |
+
if isUnknown then
|
| 660 |
+
lines := lines.push m!"-- {cmd} -- unknown option"
|
| 661 |
+
else
|
| 662 |
+
lines := lines.push cmd
|
| 663 |
+
return if lines.isEmpty then none else MessageData.joinSep lines.toList "\n"
|
| 664 |
+
|
| 665 |
+
@[builtin_command_elab Parser.Command.withExporting] def elabWithExporting : CommandElab
|
| 666 |
+
| `(Parser.Command.withExporting| #with_exporting $cmd) =>
|
| 667 |
+
withExporting do
|
| 668 |
+
elabCommand cmd
|
| 669 |
+
| _ => throwUnsupportedSyntax
|
| 670 |
+
|
| 671 |
+
@[builtin_command_elab Parser.Command.dumpAsyncEnvState] def elabDumpAsyncEnvState : CommandElab :=
|
| 672 |
+
fun _ => do
|
| 673 |
+
let env β getEnv
|
| 674 |
+
IO.eprintln (β env.dbgFormatAsyncState)
|
| 675 |
+
|
| 676 |
+
end Lean.Elab.Command
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinEvalCommand.lean
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Kyle Miller
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Util.CollectAxioms
|
| 8 |
+
import Lean.Elab.Deriving.Basic
|
| 9 |
+
import Lean.Elab.MutualDef
|
| 10 |
+
|
| 11 |
+
/-!
|
| 12 |
+
# Implementation of `#eval` command
|
| 13 |
+
-/
|
| 14 |
+
|
| 15 |
+
namespace Lean.Elab.Command
|
| 16 |
+
open Meta
|
| 17 |
+
|
| 18 |
+
register_builtin_option eval.pp : Bool := {
|
| 19 |
+
defValue := true
|
| 20 |
+
descr := "('#eval' command) enables using 'ToExpr' instances to pretty print the result, \
|
| 21 |
+
otherwise uses 'Repr' or 'ToString' instances"
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
register_builtin_option eval.type : Bool := {
|
| 25 |
+
defValue := false -- TODO: set to 'true'
|
| 26 |
+
descr := "('#eval' command) enables pretty printing the type of the result"
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
register_builtin_option eval.derive.repr : Bool := {
|
| 30 |
+
defValue := true
|
| 31 |
+
descr := "('#eval' command) enables auto-deriving 'Repr' instances as a fallback"
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
builtin_initialize
|
| 35 |
+
registerTraceClass `Elab.eval
|
| 36 |
+
|
| 37 |
+
/--
|
| 38 |
+
Elaborates the term, ensuring the result has no expression metavariables.
|
| 39 |
+
If there would be unsolved-for metavariables, tries hinting that the resulting type
|
| 40 |
+
is a monadic value with the `CommandElabM`, `TermElabM`, or `IO` monads.
|
| 41 |
+
Throws errors if the term is a proof or a type, but lifts props to `Bool` using `mkDecide`.
|
| 42 |
+
-/
|
| 43 |
+
private def elabTermForEval (term : Syntax) (expectedType? : Option Expr) : TermElabM Expr := do
|
| 44 |
+
let ty β expectedType?.getDM mkFreshTypeMVar
|
| 45 |
+
let e β Term.elabTermEnsuringType term ty
|
| 46 |
+
synthesizeWithHinting ty
|
| 47 |
+
let e β instantiateMVars e
|
| 48 |
+
if (β Term.logUnassignedUsingErrorInfos (β getMVars e)) then throwAbortTerm
|
| 49 |
+
if β isProof e then
|
| 50 |
+
throwError m!"cannot evaluate, proofs are not computationally relevant"
|
| 51 |
+
let e β if (β isProp e) then mkDecide e else pure e
|
| 52 |
+
if β isType e then
|
| 53 |
+
throwError m!"cannot evaluate, types are not computationally relevant"
|
| 54 |
+
trace[Elab.eval] "elaborated term:{indentExpr e}"
|
| 55 |
+
return e
|
| 56 |
+
where
|
| 57 |
+
/-- Try different strategies to make `Term.synthesizeSyntheticMVarsNoPostponing` succeed. -/
|
| 58 |
+
synthesizeWithHinting (ty : Expr) : TermElabM Unit := do
|
| 59 |
+
Term.synthesizeSyntheticMVarsUsingDefault
|
| 60 |
+
let s β saveState
|
| 61 |
+
try
|
| 62 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 63 |
+
catch ex =>
|
| 64 |
+
let exS β saveState
|
| 65 |
+
-- Try hinting that `ty` is a monad application.
|
| 66 |
+
for m in #[``CommandElabM, ``TermElabM, ``IO] do
|
| 67 |
+
s.restore true
|
| 68 |
+
try
|
| 69 |
+
if β isDefEq ty (β mkFreshMonadApp m) then
|
| 70 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 71 |
+
return
|
| 72 |
+
catch _ => pure ()
|
| 73 |
+
-- None of the hints worked, so throw the original error.
|
| 74 |
+
exS.restore true
|
| 75 |
+
throw ex
|
| 76 |
+
mkFreshMonadApp (n : Name) : MetaM Expr := do
|
| 77 |
+
let m β mkConstWithFreshMVarLevels n
|
| 78 |
+
let (args, _, _) β forallMetaBoundedTelescope (β inferType m) 1
|
| 79 |
+
return mkAppN m args
|
| 80 |
+
|
| 81 |
+
private def addAndCompileExprForEval (declName : Name) (value : Expr) (allowSorry := false) : TermElabM Unit := do
|
| 82 |
+
-- Use the `elabMutualDef` machinery to be able to support `let rec`.
|
| 83 |
+
-- Hack: since we are using the `TermElabM` version, we can insert the `value` as a metavariable via `exprToSyntax`.
|
| 84 |
+
-- An alternative design would be to make `elabTermForEval` into a term elaborator and elaborate the command all at once
|
| 85 |
+
-- with `unsafe def _eval := term_for_eval% $t`, which we did try, but unwanted error messages
|
| 86 |
+
-- such as "failed to infer definition type" can surface.
|
| 87 |
+
let defView := mkDefViewOfDef { isUnsafe := true }
|
| 88 |
+
(β `(Parser.Command.definition|
|
| 89 |
+
def $(mkIdent <| `_root_ ++ declName) := $(β Term.exprToSyntax value)))
|
| 90 |
+
Term.elabMutualDef #[] { header := "" } #[defView]
|
| 91 |
+
unless allowSorry do
|
| 92 |
+
let axioms β collectAxioms declName
|
| 93 |
+
if axioms.contains ``sorryAx then
|
| 94 |
+
throwError "\
|
| 95 |
+
aborting evaluation since the expression depends on the 'sorry' axiom, \
|
| 96 |
+
which can lead to runtime instability and crashes.\n\n\
|
| 97 |
+
To attempt to evaluate anyway despite the risks, use the '#eval!' command."
|
| 98 |
+
|
| 99 |
+
/--
|
| 100 |
+
Try to make a `@projFn ty inst e` application, even if it takes unfolding the type `ty` of `e` to synthesize the instance `inst`.
|
| 101 |
+
-/
|
| 102 |
+
private partial def mkDeltaInstProj (inst projFn : Name) (e : Expr) (ty? : Option Expr := none) (tryReduce : Bool := true) : MetaM Expr := do
|
| 103 |
+
let ty β ty?.getDM (inferType e)
|
| 104 |
+
if let .some inst β trySynthInstance (β mkAppM inst #[ty]) then
|
| 105 |
+
mkAppOptM projFn #[ty, inst, e]
|
| 106 |
+
else
|
| 107 |
+
let ty β whnfCore ty
|
| 108 |
+
let some ty β unfoldDefinition? ty
|
| 109 |
+
| guard tryReduce
|
| 110 |
+
-- Reducing the type is a strategy `#eval` used before the refactor of #5627.
|
| 111 |
+
-- The test lean/run/hlistOverload.lean depends on it, so we preserve the behavior.
|
| 112 |
+
let ty β reduce (skipTypes := false) ty
|
| 113 |
+
mkDeltaInstProj inst projFn e ty (tryReduce := false)
|
| 114 |
+
mkDeltaInstProj inst projFn e ty tryReduce
|
| 115 |
+
|
| 116 |
+
/-- Try to make a `toString e` application, even if it takes unfolding the type of `e` to find a `ToString` instance. -/
|
| 117 |
+
private def mkToString (e : Expr) (ty? : Option Expr := none) : MetaM Expr := do
|
| 118 |
+
mkDeltaInstProj ``ToString ``toString e ty?
|
| 119 |
+
|
| 120 |
+
/-- Try to make a `repr e` application, even if it takes unfolding the type of `e` to find a `Repr` instance. -/
|
| 121 |
+
private def mkRepr (e : Expr) (ty? : Option Expr := none) : MetaM Expr := do
|
| 122 |
+
mkDeltaInstProj ``Repr ``repr e ty?
|
| 123 |
+
|
| 124 |
+
/-- Try to make a `toExpr e` application, even if it takes unfolding the type of `e` to find a `ToExpr` instance. -/
|
| 125 |
+
private def mkToExpr (e : Expr) (ty? : Option Expr := none) : MetaM Expr := do
|
| 126 |
+
mkDeltaInstProj ``ToExpr ``toExpr e ty?
|
| 127 |
+
|
| 128 |
+
/--
|
| 129 |
+
Returns a representation of `e` using `Format`, or else fails.
|
| 130 |
+
If the `eval.derive.repr` option is true, then tries automatically deriving a `Repr` instance first.
|
| 131 |
+
Currently auto-derivation does not attempt to derive recursively.
|
| 132 |
+
-/
|
| 133 |
+
private def mkFormat (e : Expr) : MetaM Expr := do
|
| 134 |
+
mkRepr e <|> (do mkAppM ``Std.Format.text #[β mkToString e])
|
| 135 |
+
<|> do
|
| 136 |
+
if eval.derive.repr.get (β getOptions) then
|
| 137 |
+
if let .const name _ := (β whnf (β inferType e)).getAppFn then
|
| 138 |
+
try
|
| 139 |
+
trace[Elab.eval] "Attempting to derive a 'Repr' instance for '{.ofConstName name}'"
|
| 140 |
+
liftCommandElabM do applyDerivingHandlers ``Repr #[name]
|
| 141 |
+
resetSynthInstanceCache
|
| 142 |
+
return β mkRepr e
|
| 143 |
+
catch ex =>
|
| 144 |
+
trace[Elab.eval] "Failed to use derived 'Repr' instance. Exception: {ex.toMessageData}"
|
| 145 |
+
throwError m!"could not synthesize a 'Repr' or 'ToString' instance for type{indentExpr (β inferType e)}"
|
| 146 |
+
|
| 147 |
+
/--
|
| 148 |
+
Returns a representation of `e` using `MessageData`, or else fails.
|
| 149 |
+
Tries `mkFormat` if a `ToExpr` instance can't be synthesized.
|
| 150 |
+
-/
|
| 151 |
+
private def mkMessageData (e : Expr) : MetaM Expr := do
|
| 152 |
+
(do guard <| eval.pp.get (β getOptions); mkAppM ``MessageData.ofExpr #[β mkToExpr e])
|
| 153 |
+
<|> (return mkApp (mkConst ``MessageData.ofFormat) (β mkFormat e))
|
| 154 |
+
<|> do throwError m!"could not synthesize a 'ToExpr', 'Repr', or 'ToString' instance for type{indentExpr (β inferType e)}"
|
| 155 |
+
|
| 156 |
+
private structure EvalAction where
|
| 157 |
+
eval : CommandElabM MessageData
|
| 158 |
+
/-- Whether to print the result of evaluation.
|
| 159 |
+
If `some`, the expression is what type to use for the type ascription when `pp.type` is true. -/
|
| 160 |
+
printVal : Option Expr
|
| 161 |
+
|
| 162 |
+
unsafe def elabEvalCoreUnsafe (bang : Bool) (tk term : Syntax) (expectedType? : Option Expr) : CommandElabM Unit := withRef tk do
|
| 163 |
+
let declName := `_eval
|
| 164 |
+
-- `t` is either `MessageData` or `Format`, and `mkT` is for synthesizing an expression that yields a `t`.
|
| 165 |
+
-- The `toMessageData` function adapts `t` to `MessageData`.
|
| 166 |
+
let mkAct {t : Type} [Inhabited t] (toMessageData : t β MessageData) (mkT : Expr β MetaM Expr) (e : Expr) : TermElabM EvalAction := do
|
| 167 |
+
-- Create a monadic action given the name of the monad `mc`, the monad `m` itself,
|
| 168 |
+
-- and an expression `e` to evaluate in this monad.
|
| 169 |
+
-- A trick here is that `mkMAct?` makes use of `MonadEval` instances are currently available in this stage,
|
| 170 |
+
-- and we do not need them to be available in the target environment.
|
| 171 |
+
let mkMAct? (mc : Name) (m : Type β Type) [Monad m] [MonadEvalT m CommandElabM] (e : Expr) : TermElabM (Option EvalAction) := do
|
| 172 |
+
let some e β observing? (mkAppOptM ``MonadEvalT.monadEval #[none, mkConst mc, none, none, e])
|
| 173 |
+
| return none
|
| 174 |
+
let eType := e.appFn!.appArg!
|
| 175 |
+
if β isDefEq eType (mkConst ``Unit) then
|
| 176 |
+
addAndCompileExprForEval declName e (allowSorry := bang)
|
| 177 |
+
let mf : m Unit β evalConst (m Unit) declName
|
| 178 |
+
return some { eval := do MonadEvalT.monadEval mf; pure "", printVal := none }
|
| 179 |
+
else
|
| 180 |
+
let rf β withLocalDeclD `x eType fun x => do mkLambdaFVars #[x] (β mkT x)
|
| 181 |
+
let r β mkAppM ``Functor.map #[rf, e]
|
| 182 |
+
addAndCompileExprForEval declName r (allowSorry := bang)
|
| 183 |
+
let mf : m t β evalConst (m t) declName
|
| 184 |
+
return some { eval := toMessageData <$> MonadEvalT.monadEval mf, printVal := some eType }
|
| 185 |
+
if let some act β mkMAct? ``CommandElabM CommandElabM e
|
| 186 |
+
-- Fallbacks in case we are in the Lean package but don't have `CommandElabM` yet
|
| 187 |
+
<||> mkMAct? ``TermElabM TermElabM e <||> mkMAct? ``MetaM MetaM e <||> mkMAct? ``CoreM CoreM e
|
| 188 |
+
-- Fallback in case nothing is imported
|
| 189 |
+
<||> mkMAct? ``IO IO e then
|
| 190 |
+
return act
|
| 191 |
+
else
|
| 192 |
+
-- Otherwise, assume this is a pure value.
|
| 193 |
+
-- There is no need to adapt pure values to `CommandElabM`.
|
| 194 |
+
-- This enables `#eval` to work on pure values even when `CommandElabM` is not available.
|
| 195 |
+
let r β try mkT e catch ex => do
|
| 196 |
+
-- Diagnose whether the value is monadic for a representable value, since it's better to mention `MonadEval` in that case.
|
| 197 |
+
try
|
| 198 |
+
let some (m, ty) β isTypeApp? (β inferType e) | failure
|
| 199 |
+
guard <| (β isMonad? m).isSome
|
| 200 |
+
-- Verify that there is a way to form some representation:
|
| 201 |
+
discard <| withLocalDeclD `x ty fun x => mkT x
|
| 202 |
+
catch _ =>
|
| 203 |
+
throw ex
|
| 204 |
+
throwError m!"unable to synthesize '{.ofConstName ``MonadEval}' instance \
|
| 205 |
+
to adapt{indentExpr (β inferType e)}\n\
|
| 206 |
+
to '{.ofConstName ``IO}' or '{.ofConstName ``CommandElabM}'."
|
| 207 |
+
addAndCompileExprForEval declName r (allowSorry := bang)
|
| 208 |
+
-- `evalConst` may emit IO, but this is collected by `withIsolatedStreams` below.
|
| 209 |
+
let r β toMessageData <$> evalConst t declName (checkMeta := !Elab.inServer.get (β getOptions))
|
| 210 |
+
return { eval := pure r, printVal := some (β inferType e) }
|
| 211 |
+
let (output, exOrRes) β IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get (β getOptions)) do
|
| 212 |
+
try
|
| 213 |
+
-- Generate an action without executing it. We use `withoutModifyingEnv` to ensure
|
| 214 |
+
-- we don't pollute the environment with auxiliary declarations.
|
| 215 |
+
let act : EvalAction β liftTermElabM do Term.withDeclName declName do withoutModifyingEnv do
|
| 216 |
+
let e β elabTermForEval term expectedType?
|
| 217 |
+
-- If there is an elaboration error, don't evaluate!
|
| 218 |
+
if e.hasSyntheticSorry then throwAbortTerm
|
| 219 |
+
-- We want `#eval` to work even in the core library, so if `ofFormat` isn't available,
|
| 220 |
+
-- we fall back on a `Format`-based approach.
|
| 221 |
+
if (β getEnv).contains ``Lean.MessageData.ofFormat then
|
| 222 |
+
mkAct id (mkMessageData Β·) e
|
| 223 |
+
else
|
| 224 |
+
mkAct Lean.MessageData.ofFormat (mkFormat Β·) e
|
| 225 |
+
let res β act.eval
|
| 226 |
+
return Sum.inr (res, act.printVal)
|
| 227 |
+
catch ex =>
|
| 228 |
+
return Sum.inl ex
|
| 229 |
+
if !output.isEmpty then logInfoAt tk output
|
| 230 |
+
match exOrRes with
|
| 231 |
+
| .inl ex => logException ex
|
| 232 |
+
| .inr (_, none) => pure ()
|
| 233 |
+
| .inr (res, some type) =>
|
| 234 |
+
if eval.type.get (β getOptions) then
|
| 235 |
+
logInfo m!"{res} : {type}"
|
| 236 |
+
else
|
| 237 |
+
logInfo res
|
| 238 |
+
|
| 239 |
+
@[implemented_by elabEvalCoreUnsafe]
|
| 240 |
+
opaque elabEvalCore (bang : Bool) (tk term : Syntax) (expectedType? : Option Expr) : CommandElabM Unit
|
| 241 |
+
|
| 242 |
+
@[builtin_command_elab Β«evalΒ»]
|
| 243 |
+
def elabEval : CommandElab
|
| 244 |
+
| `(#eval%$tk $term) => elabEvalCore false tk term none
|
| 245 |
+
| _ => throwUnsupportedSyntax
|
| 246 |
+
|
| 247 |
+
@[builtin_command_elab evalBang]
|
| 248 |
+
def elabEvalBang : CommandElab
|
| 249 |
+
| `(#eval!%$tk $term) => elabEvalCore true tk term none
|
| 250 |
+
| _ => throwUnsupportedSyntax
|
| 251 |
+
|
| 252 |
+
@[builtin_command_elab runCmd]
|
| 253 |
+
def elabRunCmd : CommandElab
|
| 254 |
+
| `(run_cmd%$tk $elems:doSeq) => do
|
| 255 |
+
unless (β getEnv).contains ``CommandElabM do
|
| 256 |
+
throwError "to use this command, include `import Lean.Elab.Command`"
|
| 257 |
+
elabEvalCore false tk (β `(discard do $elems)) (mkApp (mkConst ``CommandElabM) (mkConst ``Unit))
|
| 258 |
+
| _ => throwUnsupportedSyntax
|
| 259 |
+
|
| 260 |
+
@[builtin_command_elab runElab]
|
| 261 |
+
def elabRunElab : CommandElab
|
| 262 |
+
| `(run_elab%$tk $elems:doSeq) => do
|
| 263 |
+
unless (β getEnv).contains ``TermElabM do
|
| 264 |
+
throwError "to use this command, include `import Lean.Elab.Term`"
|
| 265 |
+
elabEvalCore false tk (β `(discard do $elems)) (mkApp (mkConst ``TermElabM) (mkConst ``Unit))
|
| 266 |
+
| _ => throwUnsupportedSyntax
|
| 267 |
+
|
| 268 |
+
@[builtin_command_elab runMeta]
|
| 269 |
+
def elabRunMeta : CommandElab := fun stx =>
|
| 270 |
+
match stx with
|
| 271 |
+
| `(run_meta%$tk $elems:doSeq) => do
|
| 272 |
+
unless (β getEnv).contains ``MetaM do
|
| 273 |
+
throwError "to use this command, include `import Lean.Meta.Basic`"
|
| 274 |
+
elabEvalCore false tk (β `(discard do $elems)) (mkApp (mkConst ``MetaM) (mkConst ``Unit))
|
| 275 |
+
| _ => throwUnsupportedSyntax
|
| 276 |
+
|
| 277 |
+
end Lean.Elab.Command
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinNotation.lean
ADDED
|
@@ -0,0 +1,534 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Gabriel Ebner
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.BorrowedAnnotation
|
| 8 |
+
import Lean.Meta.KAbstract
|
| 9 |
+
import Lean.Meta.Closure
|
| 10 |
+
import Lean.Meta.MatchUtil
|
| 11 |
+
import Lean.Compiler.ImplementedByAttr
|
| 12 |
+
import Lean.Elab.SyntheticMVars
|
| 13 |
+
import Lean.Elab.Eval
|
| 14 |
+
import Lean.Elab.Binders
|
| 15 |
+
|
| 16 |
+
namespace Lean.Elab.Term
|
| 17 |
+
open Meta
|
| 18 |
+
|
| 19 |
+
@[builtin_term_elab coeNotation] def elabCoe : TermElab := fun stx expectedType? => do
|
| 20 |
+
let stx := stx[1]
|
| 21 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 22 |
+
let e β elabTerm stx none
|
| 23 |
+
if expectedType?.isNone then
|
| 24 |
+
throwError "invalid coercion notation, expected type is not known"
|
| 25 |
+
ensureHasType expectedType? e
|
| 26 |
+
|
| 27 |
+
@[builtin_term_elab coeFunNotation] def elabCoeFunNotation : TermElab := fun stx _ => do
|
| 28 |
+
let x β elabTerm stx[1] none
|
| 29 |
+
if let some ty β coerceToFunction? x then
|
| 30 |
+
return ty
|
| 31 |
+
else
|
| 32 |
+
throwError "cannot coerce to function{indentExpr x}"
|
| 33 |
+
|
| 34 |
+
@[builtin_term_elab coeSortNotation] def elabCoeSortNotation : TermElab := fun stx _ => do
|
| 35 |
+
let x β elabTerm stx[1] none
|
| 36 |
+
if let some ty β coerceToSort? x then
|
| 37 |
+
return ty
|
| 38 |
+
else
|
| 39 |
+
throwError "cannot coerce to sort{indentExpr x}"
|
| 40 |
+
|
| 41 |
+
@[builtin_term_elab anonymousCtor] def elabAnonymousCtor : TermElab := fun stx expectedType? =>
|
| 42 |
+
match stx with
|
| 43 |
+
| `(β¨$args,*β©) => do
|
| 44 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 45 |
+
match expectedType? with
|
| 46 |
+
| some expectedType =>
|
| 47 |
+
let expectedType β whnf expectedType
|
| 48 |
+
matchConstInduct expectedType.getAppFn
|
| 49 |
+
(fun _ => throwError "invalid constructor β¨...β©, expected type must be an inductive type {indentExpr expectedType}")
|
| 50 |
+
(fun ival _ => do
|
| 51 |
+
match ival.ctors with
|
| 52 |
+
| [ctor] =>
|
| 53 |
+
if isPrivateNameFromImportedModule (β getEnv) ctor then
|
| 54 |
+
throwError "invalid β¨...β© notation, constructor for `{ival.name}` is marked as private"
|
| 55 |
+
let cinfo β getConstInfoCtor ctor
|
| 56 |
+
let numExplicitFields β forallTelescopeReducing cinfo.type fun xs _ => do
|
| 57 |
+
let mut n := 0
|
| 58 |
+
for h : i in [cinfo.numParams:xs.size] do
|
| 59 |
+
if (β getFVarLocalDecl xs[i]).binderInfo.isExplicit then
|
| 60 |
+
n := n + 1
|
| 61 |
+
return n
|
| 62 |
+
let args := args.getElems
|
| 63 |
+
if args.size < numExplicitFields then
|
| 64 |
+
throwError "invalid constructor β¨...β©, insufficient number of arguments, constructs '{ctor}' has #{numExplicitFields} explicit fields, but only #{args.size} provided"
|
| 65 |
+
let newStx β if args.size == numExplicitFields then
|
| 66 |
+
`($(mkCIdentFrom stx ctor (canonical := true)) $(args)*)
|
| 67 |
+
else if numExplicitFields == 0 then
|
| 68 |
+
throwError "invalid constructor β¨...β©, insufficient number of arguments, constructs '{ctor}' does not have explicit fields, but #{args.size} provided"
|
| 69 |
+
else
|
| 70 |
+
let extra := args[(numExplicitFields-1)...args.size]
|
| 71 |
+
let newLast β `(β¨$[$extra],*β©)
|
| 72 |
+
let newArgs := args[*...(numExplicitFields-1)].toArray.push newLast
|
| 73 |
+
`($(mkCIdentFrom stx ctor (canonical := true)) $(newArgs)*)
|
| 74 |
+
withMacroExpansion stx newStx $ elabTerm newStx expectedType?
|
| 75 |
+
| _ => throwError "invalid constructor β¨...β©, expected type must be an inductive type with only one constructor {indentExpr expectedType}")
|
| 76 |
+
| none => throwError "invalid constructor β¨...β©, expected type must be known"
|
| 77 |
+
| _ => throwUnsupportedSyntax
|
| 78 |
+
|
| 79 |
+
@[builtin_term_elab borrowed] def elabBorrowed : TermElab := fun stx expectedType? =>
|
| 80 |
+
match stx with
|
| 81 |
+
| `(@& $e) => return markBorrowed (β elabTerm e expectedType?)
|
| 82 |
+
| _ => throwUnsupportedSyntax
|
| 83 |
+
|
| 84 |
+
@[builtin_macro Lean.Parser.Term.show] def expandShow : Macro := fun stx =>
|
| 85 |
+
match stx with
|
| 86 |
+
| `(show $type by%$b $tac) => `(show $type from by%$b $tac)
|
| 87 |
+
| _ => Macro.throwUnsupported
|
| 88 |
+
|
| 89 |
+
@[builtin_term_elab Lean.Parser.Term.show] def elabShow : TermElab := fun stx expectedType? => do
|
| 90 |
+
match stx with
|
| 91 |
+
| `(show $type from $val) =>
|
| 92 |
+
/-
|
| 93 |
+
We first elaborate the type and try to unify it with the expected type if available.
|
| 94 |
+
Note that, we should not throw an error if the types do not unify. Recall that we have coercions and
|
| 95 |
+
the following is supported in Lean 3 and 4.
|
| 96 |
+
```
|
| 97 |
+
example : Int :=
|
| 98 |
+
show Nat from 0
|
| 99 |
+
```
|
| 100 |
+
-/
|
| 101 |
+
let type β withSynthesize (postpone := .yes) do
|
| 102 |
+
let type β elabType type
|
| 103 |
+
if let some expectedType := expectedType? then
|
| 104 |
+
-- Recall that a similar approach is used when elaborating applications
|
| 105 |
+
discard <| isDefEq expectedType type
|
| 106 |
+
return type
|
| 107 |
+
/-
|
| 108 |
+
Recall that we do not use the same approach used to elaborate type ascriptions.
|
| 109 |
+
For the `($val : $type)` notation, we just elaborate `val` using `type` and
|
| 110 |
+
ensure it has type `type`. This approach only ensure the type resulting expression
|
| 111 |
+
is definitionally equal to `type`. For the `show` notation we use `have` to ensure the type
|
| 112 |
+
of the resulting expression is *structurally equal* `type`. Structural equality is important,
|
| 113 |
+
for example, if the resulting expression is a `simp`/`rw` parameter. Here is an example:
|
| 114 |
+
```
|
| 115 |
+
example (x : Nat) : (x + 0) + y = x + y := by
|
| 116 |
+
rw [show x + 0 = x from rfl]
|
| 117 |
+
```
|
| 118 |
+
-/
|
| 119 |
+
let thisId := mkIdentFrom stx `this
|
| 120 |
+
let valNew β `(have $thisId:ident : $(β exprToSyntax type) := $val; $thisId)
|
| 121 |
+
elabTerm valNew expectedType?
|
| 122 |
+
| _ => throwUnsupportedSyntax
|
| 123 |
+
|
| 124 |
+
@[builtin_macro Lean.Parser.Term.suffices] def expandSuffices : Macro
|
| 125 |
+
| `(suffices%$tk $x:ident : $type from $val; $body) => `(have%$tk $x:ident : $type := $body; $val)
|
| 126 |
+
| `(suffices%$tk _%$x : $type from $val; $body) => `(have%$tk _%$x : $type := $body; $val)
|
| 127 |
+
| `(suffices%$tk $hy:hygieneInfo $type from $val; $body) => `(have%$tk $hy:hygieneInfo : $type := $body; $val)
|
| 128 |
+
| `(suffices%$tk $x:ident : $type $b:byTactic'; $body) =>
|
| 129 |
+
-- Pass on `SourceInfo` of `b` to `have`. This is necessary to display the goal state in the
|
| 130 |
+
-- trailing whitespace of `by` and sound since `byTactic` and `byTactic'` are identical.
|
| 131 |
+
let b := β¨b.raw.setKind `Lean.Parser.Term.byTacticβ©
|
| 132 |
+
`(have%$tk $x:ident : $type := $body; $b:byTactic)
|
| 133 |
+
| `(suffices%$tk _%$x : $type $b:byTactic'; $body) =>
|
| 134 |
+
let b := β¨b.raw.setKind `Lean.Parser.Term.byTacticβ©
|
| 135 |
+
`(have%$tk _%$x : $type := $body; $b:byTactic)
|
| 136 |
+
| `(suffices%$tk $hy:hygieneInfo $type $b:byTactic'; $body) =>
|
| 137 |
+
let b := β¨b.raw.setKind `Lean.Parser.Term.byTacticβ©
|
| 138 |
+
`(have%$tk $hy:hygieneInfo : $type := $body; $b:byTactic)
|
| 139 |
+
| _ => Macro.throwUnsupported
|
| 140 |
+
|
| 141 |
+
open Lean.Parser in
|
| 142 |
+
private def elabParserMacroAux (prec e : Term) (withAnonymousAntiquot : Bool) : TermElabM Syntax := do
|
| 143 |
+
let (some declName) β getDeclName?
|
| 144 |
+
| throwError "invalid `leading_parser` macro, it must be used in definitions"
|
| 145 |
+
match extractMacroScopes declName with
|
| 146 |
+
| { name := .str _ s, .. } =>
|
| 147 |
+
let kind := quote declName
|
| 148 |
+
let mut p β ``(withAntiquot
|
| 149 |
+
(mkAntiquot $(quote s) $kind $(quote withAnonymousAntiquot))
|
| 150 |
+
(leadingNode $kind $prec $e))
|
| 151 |
+
-- cache only unparameterized parsers
|
| 152 |
+
if (β getLCtx).all (Β·.isAuxDecl) then
|
| 153 |
+
p β ``(withCache $kind $p)
|
| 154 |
+
return p
|
| 155 |
+
| _ => throwError "invalid `leading_parser` macro, unexpected declaration name"
|
| 156 |
+
|
| 157 |
+
@[builtin_term_elab Β«leading_parserΒ»] def elabLeadingParserMacro : TermElab :=
|
| 158 |
+
adaptExpander fun
|
| 159 |
+
| `(leading_parser $[: $prec?]? $[(withAnonymousAntiquot := $anon?)]? $e) =>
|
| 160 |
+
elabParserMacroAux (prec?.getD (quote Parser.maxPrec)) e (anon?.all (Β·.raw.isOfKind ``Parser.Term.trueVal))
|
| 161 |
+
| _ => throwUnsupportedSyntax
|
| 162 |
+
|
| 163 |
+
private def elabTParserMacroAux (prec lhsPrec e : Term) : TermElabM Syntax := do
|
| 164 |
+
let declName? β getDeclName?
|
| 165 |
+
match declName? with
|
| 166 |
+
| some declName => let kind := quote declName; ``(Lean.Parser.trailingNode $kind $prec $lhsPrec $e)
|
| 167 |
+
| none => throwError "invalid `trailing_parser` macro, it must be used in definitions"
|
| 168 |
+
|
| 169 |
+
@[builtin_term_elab Β«trailing_parserΒ»] def elabTrailingParserMacro : TermElab :=
|
| 170 |
+
adaptExpander fun stx => match stx with
|
| 171 |
+
| `(trailing_parser$[:$prec?]?$[:$lhsPrec?]? $e) =>
|
| 172 |
+
elabTParserMacroAux (prec?.getD <| quote Parser.maxPrec) (lhsPrec?.getD <| quote 0) e
|
| 173 |
+
| _ => throwUnsupportedSyntax
|
| 174 |
+
|
| 175 |
+
@[builtin_term_elab Lean.Parser.Term.panic] def elabPanic : TermElab := fun stx expectedType? => do
|
| 176 |
+
match stx with
|
| 177 |
+
| `(panic! $arg) =>
|
| 178 |
+
let pos β getRefPosition
|
| 179 |
+
let env β getEnv
|
| 180 |
+
let stxNew β match (β getDeclName?) with
|
| 181 |
+
| some declName => `(panicWithPosWithDecl $(quote (toString env.mainModule)) $(quote (toString declName)) $(quote pos.line) $(quote pos.column) $arg)
|
| 182 |
+
| none => `(panicWithPos $(quote (toString env.mainModule)) $(quote pos.line) $(quote pos.column) $arg)
|
| 183 |
+
withMacroExpansion stx stxNew $ elabTerm stxNew expectedType?
|
| 184 |
+
| _ => throwUnsupportedSyntax
|
| 185 |
+
|
| 186 |
+
@[builtin_macro Lean.Parser.Term.unreachable] def expandUnreachable : Macro := fun _ =>
|
| 187 |
+
`(panic! "unreachable code has been reached")
|
| 188 |
+
|
| 189 |
+
@[builtin_macro Lean.Parser.Term.assert] def expandAssert : Macro
|
| 190 |
+
| `(assert! $cond; $body) =>
|
| 191 |
+
match cond.raw.reprint with
|
| 192 |
+
| some code => `(if $cond then $body else panic! ("assertion violation: " ++ $(quote code)))
|
| 193 |
+
| none => `(if $cond then $body else panic! ("assertion violation"))
|
| 194 |
+
| _ => Macro.throwUnsupported
|
| 195 |
+
|
| 196 |
+
register_builtin_option debugAssertions : Bool := {
|
| 197 |
+
defValue := false
|
| 198 |
+
descr := "enable `debug_assert!` statements\
|
| 199 |
+
\n\
|
| 200 |
+
\nDefaults to `false` unless the Lake `buildType` is `debug`."
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
@[builtin_term_elab Lean.Parser.Term.debugAssert] def elabDebugAssert : TermElab :=
|
| 204 |
+
adaptExpander fun
|
| 205 |
+
| `(Parser.Term.debugAssert| debug_assert! $cond; $body) => do
|
| 206 |
+
if debugAssertions.get (β getOptions) then
|
| 207 |
+
`(assert! $cond; $body)
|
| 208 |
+
else
|
| 209 |
+
return body
|
| 210 |
+
| _ => throwUnsupportedSyntax
|
| 211 |
+
|
| 212 |
+
@[builtin_macro Lean.Parser.Term.dbgTrace] def expandDbgTrace : Macro
|
| 213 |
+
| `(dbg_trace $arg:interpolatedStr; $body) => `(dbgTrace (s! $arg) fun _ => $body)
|
| 214 |
+
| `(dbg_trace $arg:term; $body) => `(dbgTrace (toString $arg) fun _ => $body)
|
| 215 |
+
| _ => Macro.throwUnsupported
|
| 216 |
+
|
| 217 |
+
@[builtin_term_elab Β«sorryΒ»] def elabSorry : TermElab := fun _ expectedType? => do
|
| 218 |
+
let type β expectedType?.getDM mkFreshTypeMVar
|
| 219 |
+
mkLabeledSorry type (synthetic := false) (unique := true)
|
| 220 |
+
|
| 221 |
+
/-- Return syntax `Prod.mk elems[0] (Prod.mk elems[1] ... (Prod.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
|
| 222 |
+
partial def mkPairs (elems : Array Term) : MacroM Term :=
|
| 223 |
+
let rec loop (i : Nat) (acc : Term) := do
|
| 224 |
+
if i > 0 then
|
| 225 |
+
let i := i - 1
|
| 226 |
+
let elem := elems[i]!
|
| 227 |
+
let acc β `(Prod.mk $elem $acc)
|
| 228 |
+
loop i acc
|
| 229 |
+
else
|
| 230 |
+
pure acc
|
| 231 |
+
loop (elems.size - 1) elems.back!
|
| 232 |
+
|
| 233 |
+
/-- Return syntax `PProd.mk elems[0] (PProd.mk elems[1] ... (PProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
|
| 234 |
+
partial def mkPPairs (elems : Array Term) : MacroM Term :=
|
| 235 |
+
let rec loop (i : Nat) (acc : Term) := do
|
| 236 |
+
if i > 0 then
|
| 237 |
+
let i := i - 1
|
| 238 |
+
let elem := elems[i]!
|
| 239 |
+
let acc β `(PProd.mk $elem $acc)
|
| 240 |
+
loop i acc
|
| 241 |
+
else
|
| 242 |
+
pure acc
|
| 243 |
+
loop (elems.size - 1) elems.back!
|
| 244 |
+
|
| 245 |
+
/-- Return syntax `MProd.mk elems[0] (MProd.mk elems[1] ... (MProd.mk elems[elems.size - 2] elems[elems.size - 1])))` -/
|
| 246 |
+
partial def mkMPairs (elems : Array Term) : MacroM Term :=
|
| 247 |
+
let rec loop (i : Nat) (acc : Term) := do
|
| 248 |
+
if i > 0 then
|
| 249 |
+
let i := i - 1
|
| 250 |
+
let elem := elems[i]!
|
| 251 |
+
let acc β `(MProd.mk $elem $acc)
|
| 252 |
+
loop i acc
|
| 253 |
+
else
|
| 254 |
+
pure acc
|
| 255 |
+
loop (elems.size - 1) elems.back!
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
open Parser in
|
| 259 |
+
partial def hasCDot : Syntax β Bool
|
| 260 |
+
| Syntax.node _ k args =>
|
| 261 |
+
if k == ``Term.paren || k == ``Term.typeAscription || k == ``Term.tuple then false
|
| 262 |
+
else if k == ``Term.cdot then true
|
| 263 |
+
else args.any hasCDot
|
| 264 |
+
| _ => false
|
| 265 |
+
|
| 266 |
+
/--
|
| 267 |
+
Return `some` if succeeded expanding `Β·` notation occurring in
|
| 268 |
+
the given syntax. Otherwise, return `none`.
|
| 269 |
+
Examples:
|
| 270 |
+
- `Β· + 1` => `fun x => x + 1`
|
| 271 |
+
- `f Β· Β· b` => `fun x1 x2 => f x1 x2 b` -/
|
| 272 |
+
partial def expandCDot? (stx : Term) : MacroM (Option Term) := do
|
| 273 |
+
if hasCDot stx then
|
| 274 |
+
withFreshMacroScope do
|
| 275 |
+
let mut (newStx, binders) β (go stx).run #[]
|
| 276 |
+
if binders.size == 1 then
|
| 277 |
+
-- It is nicer using `x` over `x1` if there's only a single binder.
|
| 278 |
+
let x1 := binders[0]!
|
| 279 |
+
let x := mkIdentFrom x1 (β MonadQuotation.addMacroScope `x) (canonical := true)
|
| 280 |
+
binders := binders.set! 0 x
|
| 281 |
+
newStx β newStx.replaceM fun s => pure (if s == x1 then x else none)
|
| 282 |
+
`(fun $binders* => $(β¨newStxβ©))
|
| 283 |
+
else
|
| 284 |
+
pure none
|
| 285 |
+
where
|
| 286 |
+
/--
|
| 287 |
+
Auxiliary function for expanding the `Β·` notation.
|
| 288 |
+
The extra state `Array Syntax` contains the new binder names.
|
| 289 |
+
If `stx` is a `Β·`, we create a fresh identifier, store it in the
|
| 290 |
+
extra state, and return it. Otherwise, we just return `stx`.
|
| 291 |
+
-/
|
| 292 |
+
go : Syntax β StateT (Array Ident) MacroM Syntax
|
| 293 |
+
| stx@`(($(_))) => pure stx
|
| 294 |
+
| stx@`(Β·) => do
|
| 295 |
+
let name β MonadQuotation.addMacroScope <| Name.mkSimple s!"x{(β get).size + 1}"
|
| 296 |
+
let id := mkIdentFrom stx name (canonical := true)
|
| 297 |
+
modify (fun s => s.push id)
|
| 298 |
+
pure id
|
| 299 |
+
| stx => match stx with
|
| 300 |
+
| .node _ k args => do
|
| 301 |
+
let args β
|
| 302 |
+
if k == choiceKind then
|
| 303 |
+
if args.isEmpty then
|
| 304 |
+
return stx
|
| 305 |
+
let s β get
|
| 306 |
+
let args' β args.mapM (fun arg => go arg |>.run s)
|
| 307 |
+
let s' := args'[0]!.2
|
| 308 |
+
unless args'.all (fun (_, s'') => s''.size == s'.size) do
|
| 309 |
+
Macro.throwErrorAt stx "Ambiguous notation in cdot function has different numbers of 'Β·' arguments in each alternative."
|
| 310 |
+
set s'
|
| 311 |
+
pure <| args'.map Prod.fst
|
| 312 |
+
else
|
| 313 |
+
args.mapM go
|
| 314 |
+
return .node (.fromRef stx (canonical := true)) k args
|
| 315 |
+
| _ => pure stx
|
| 316 |
+
|
| 317 |
+
/--
|
| 318 |
+
Helper method for elaborating terms such as `(.+.)` where a constant name is expected.
|
| 319 |
+
This method is usually used to implement tactics that take function names as arguments
|
| 320 |
+
(e.g., `simp`).
|
| 321 |
+
-/
|
| 322 |
+
def elabCDotFunctionAlias? (stx : Term) : TermElabM (Option Expr) := do
|
| 323 |
+
let some stx β liftMacroM <| expandCDotArg? stx | pure none
|
| 324 |
+
let stx β liftMacroM <| expandMacros stx
|
| 325 |
+
match stx with
|
| 326 |
+
| `(fun $binders* => $f $args*) =>
|
| 327 |
+
if binders.raw.toList.isPerm args.raw.toList then
|
| 328 |
+
try Term.resolveId? f catch _ => return none
|
| 329 |
+
else
|
| 330 |
+
return none
|
| 331 |
+
| `(fun $binders* => binop% $f $a $b)
|
| 332 |
+
| `(fun $binders* => binop_lazy% $f $a $b)
|
| 333 |
+
| `(fun $binders* => leftact% $f $a $b)
|
| 334 |
+
| `(fun $binders* => rightact% $f $a $b)
|
| 335 |
+
| `(fun $binders* => binrel% $f $a $b)
|
| 336 |
+
| `(fun $binders* => binrel_no_prop% $f $a $b) =>
|
| 337 |
+
if binders == #[a, b] || binders == #[b, a] then
|
| 338 |
+
try Term.resolveId? f catch _ => return none
|
| 339 |
+
else
|
| 340 |
+
return none
|
| 341 |
+
| `(fun $binders* => unop% $f $a) =>
|
| 342 |
+
if binders == #[a] then
|
| 343 |
+
try Term.resolveId? f catch _ => return none
|
| 344 |
+
else
|
| 345 |
+
return none
|
| 346 |
+
| _ => return none
|
| 347 |
+
where
|
| 348 |
+
expandCDotArg? (stx : Term) : MacroM (Option Term) :=
|
| 349 |
+
match stx with
|
| 350 |
+
| `(($e)) => Term.expandCDot? e
|
| 351 |
+
| _ => Term.expandCDot? stx
|
| 352 |
+
|
| 353 |
+
@[builtin_macro Lean.Parser.Term.paren] def expandParen : Macro
|
| 354 |
+
| `(($e)) => return (β expandCDot? e).getD e
|
| 355 |
+
| _ => Macro.throwUnsupported
|
| 356 |
+
|
| 357 |
+
@[builtin_macro Lean.Parser.Term.tuple] def expandTuple : Macro
|
| 358 |
+
| `(()) => ``(Unit.unit)
|
| 359 |
+
| `(($e, $es,*)) => do
|
| 360 |
+
let pairs β mkPairs (#[e] ++ es)
|
| 361 |
+
return (β expandCDot? pairs).getD pairs
|
| 362 |
+
| _ => Macro.throwUnsupported
|
| 363 |
+
|
| 364 |
+
@[builtin_macro Lean.Parser.Term.typeAscription] def expandTypeAscription : Macro
|
| 365 |
+
| `(($e : $(type)?)) => do
|
| 366 |
+
match (β expandCDot? e) with
|
| 367 |
+
| some e => `(($e : $(type)?))
|
| 368 |
+
| none => Macro.throwUnsupported
|
| 369 |
+
| _ => Macro.throwUnsupported
|
| 370 |
+
|
| 371 |
+
@[builtin_term_elab typeAscription] def elabTypeAscription : TermElab
|
| 372 |
+
| `(($e : $type)), _ => do
|
| 373 |
+
let type β withSynthesize (postpone := .yes) <| elabType type
|
| 374 |
+
let e β elabTerm e type
|
| 375 |
+
ensureHasType type e
|
| 376 |
+
| `(($e :)), expectedType? => do
|
| 377 |
+
let e β withSynthesize (postpone := .no) <| elabTerm e none
|
| 378 |
+
ensureHasType expectedType? e
|
| 379 |
+
| _, _ => throwUnsupportedSyntax
|
| 380 |
+
|
| 381 |
+
/-- Return `true` if `lhs` is a free variable and `rhs` does not depend on it. -/
|
| 382 |
+
private def isSubstCandidate (lhs rhs : Expr) : MetaM Bool :=
|
| 383 |
+
if lhs.isFVar then
|
| 384 |
+
return !(β dependsOn rhs lhs.fvarId!)
|
| 385 |
+
else
|
| 386 |
+
return false
|
| 387 |
+
|
| 388 |
+
/--
|
| 389 |
+
Given an expression `e` that is the elaboration of `stx`, if `e` is a free variable, then return `k stx`.
|
| 390 |
+
Otherwise, return `(fun x => k x) e`
|
| 391 |
+
-/
|
| 392 |
+
private def withLocalIdentFor (stx : Term) (e : Expr) (k : Term β TermElabM Expr) : TermElabM Expr := do
|
| 393 |
+
if e.isFVar then
|
| 394 |
+
k stx
|
| 395 |
+
else
|
| 396 |
+
let id β mkFreshUserName `h
|
| 397 |
+
let aux β withLocalDeclD id (β inferType e) fun x => do mkLambdaFVars #[x] (β k (mkIdentFrom stx id))
|
| 398 |
+
return mkApp aux e
|
| 399 |
+
|
| 400 |
+
@[builtin_term_elab subst] def elabSubst : TermElab := fun stx expectedType? => do
|
| 401 |
+
let expectedType? β tryPostponeIfHasMVars? expectedType?
|
| 402 |
+
match stx with
|
| 403 |
+
| `($heqStx βΈ $hStx) => do
|
| 404 |
+
synthesizeSyntheticMVars
|
| 405 |
+
let mut heq β withSynthesize <| elabTerm heqStx none
|
| 406 |
+
let heqType β inferType heq
|
| 407 |
+
let heqType β instantiateMVars heqType
|
| 408 |
+
match (β Meta.matchEq? heqType) with
|
| 409 |
+
| none => throwError "invalid `βΈ` notation, argument{indentExpr heq}\nhas type{indentExpr heqType}\nequality expected"
|
| 410 |
+
| some (Ξ±, lhs, rhs) =>
|
| 411 |
+
let mut lhs := lhs
|
| 412 |
+
let mut rhs := rhs
|
| 413 |
+
let mkMotive (lhs typeWithLooseBVar : Expr) := do
|
| 414 |
+
withLocalDeclD (β mkFreshUserName `x) Ξ± fun x => do
|
| 415 |
+
withLocalDeclD (β mkFreshUserName `h) (β mkEq lhs x) fun h => do
|
| 416 |
+
mkLambdaFVars #[x, h] $ typeWithLooseBVar.instantiate1 x
|
| 417 |
+
match expectedType? with
|
| 418 |
+
| some expectedType =>
|
| 419 |
+
let mut expectedAbst β kabstract expectedType rhs
|
| 420 |
+
unless expectedAbst.hasLooseBVars do
|
| 421 |
+
expectedAbst β kabstract expectedType lhs
|
| 422 |
+
unless expectedAbst.hasLooseBVars do
|
| 423 |
+
throwError "invalid `βΈ` notation, expected result type of cast is {indentExpr expectedType}\nhowever, the equality {indentExpr heq}\nof type {indentExpr heqType}\ndoes not contain the expected result type on either the left or the right hand side"
|
| 424 |
+
heq β mkEqSymm heq
|
| 425 |
+
(lhs, rhs) := (rhs, lhs)
|
| 426 |
+
let hExpectedType := expectedAbst.instantiate1 lhs
|
| 427 |
+
let (h, badMotive?) β withRef hStx do
|
| 428 |
+
let h β elabTerm hStx hExpectedType
|
| 429 |
+
try
|
| 430 |
+
return (β ensureHasType hExpectedType h, none)
|
| 431 |
+
catch ex =>
|
| 432 |
+
-- if `rhs` occurs in `hType`, we try to apply `heq` to `h` too
|
| 433 |
+
let hType β inferType h
|
| 434 |
+
let hTypeAbst β kabstract hType rhs
|
| 435 |
+
unless hTypeAbst.hasLooseBVars do
|
| 436 |
+
throw ex
|
| 437 |
+
let hTypeNew := hTypeAbst.instantiate1 lhs
|
| 438 |
+
unless (β isDefEq hExpectedType hTypeNew) do
|
| 439 |
+
throw ex
|
| 440 |
+
let motive β mkMotive rhs hTypeAbst
|
| 441 |
+
if !(β isTypeCorrect motive) then
|
| 442 |
+
return (h, some motive)
|
| 443 |
+
else
|
| 444 |
+
return (β mkEqRec motive h (β mkEqSymm heq), none)
|
| 445 |
+
let motive β mkMotive lhs expectedAbst
|
| 446 |
+
if badMotive?.isSome || !(β isTypeCorrect motive) then
|
| 447 |
+
-- Before failing try to use `subst`
|
| 448 |
+
if β (isSubstCandidate lhs rhs <||> isSubstCandidate rhs lhs) then
|
| 449 |
+
withLocalIdentFor heqStx heq fun heqStx => do
|
| 450 |
+
let h β instantiateMVars h
|
| 451 |
+
if h.hasMVar then
|
| 452 |
+
-- If `h` has metavariables, we try to elaborate `hStx` again after we substitute `heqStx`
|
| 453 |
+
-- Remark: re-elaborating `hStx` may be problematic if `hStx` contains the `lhs` of `heqStx` which will be eliminated by `subst`
|
| 454 |
+
let stxNew β `(by subst $heqStx; exact $hStx)
|
| 455 |
+
withMacroExpansion stx stxNew (elabTerm stxNew expectedType)
|
| 456 |
+
else
|
| 457 |
+
withLocalIdentFor hStx h fun hStx => do
|
| 458 |
+
let stxNew β `(by subst $heqStx; exact $hStx)
|
| 459 |
+
withMacroExpansion stx stxNew (elabTerm stxNew expectedType)
|
| 460 |
+
else
|
| 461 |
+
throwError "invalid `βΈ` notation, failed to compute motive for the substitution"
|
| 462 |
+
else
|
| 463 |
+
mkEqRec motive h heq
|
| 464 |
+
| none =>
|
| 465 |
+
let h β elabTerm hStx none
|
| 466 |
+
let hType β inferType h
|
| 467 |
+
let mut hTypeAbst β kabstract hType lhs
|
| 468 |
+
unless hTypeAbst.hasLooseBVars do
|
| 469 |
+
hTypeAbst β kabstract hType rhs
|
| 470 |
+
unless hTypeAbst.hasLooseBVars do
|
| 471 |
+
throwError "invalid `βΈ` notation, the equality{indentExpr heq}\nhas type {indentExpr heqType}\nbut neither side of the equality is mentioned in the type{indentExpr hType}"
|
| 472 |
+
heq β mkEqSymm heq
|
| 473 |
+
(lhs, rhs) := (rhs, lhs)
|
| 474 |
+
let motive β mkMotive lhs hTypeAbst
|
| 475 |
+
unless (β isTypeCorrect motive) do
|
| 476 |
+
throwError "invalid `βΈ` notation, failed to compute motive for the substitution"
|
| 477 |
+
mkEqRec motive h heq
|
| 478 |
+
| _ => throwUnsupportedSyntax
|
| 479 |
+
|
| 480 |
+
@[builtin_term_elab stateRefT] def elabStateRefT : TermElab := fun stx _ => do
|
| 481 |
+
let Ο β elabType stx[1]
|
| 482 |
+
let mut mStx := stx[2]
|
| 483 |
+
if mStx.getKind == ``Lean.Parser.Term.macroDollarArg then
|
| 484 |
+
mStx := mStx[1]
|
| 485 |
+
let m β elabTerm mStx (β mkArrow (mkSort levelOne) (mkSort levelOne))
|
| 486 |
+
let Ο β mkFreshExprMVar (mkSort levelOne)
|
| 487 |
+
let stWorld β mkAppM ``STWorld #[Ο, m]
|
| 488 |
+
discard <| mkInstMVar stWorld
|
| 489 |
+
mkAppM ``StateRefT' #[Ο, Ο, m]
|
| 490 |
+
|
| 491 |
+
@[builtin_term_elab noindex] def elabNoindex : TermElab := fun stx expectedType? => do
|
| 492 |
+
let e β elabTerm stx[1] expectedType?
|
| 493 |
+
return DiscrTree.mkNoindexAnnotation e
|
| 494 |
+
|
| 495 |
+
@[builtin_term_elab Β«unsafeΒ»]
|
| 496 |
+
def elabUnsafe : TermElab := fun stx expectedType? =>
|
| 497 |
+
match stx with
|
| 498 |
+
| `(unsafe $t) => do
|
| 499 |
+
let t β elabTermAndSynthesize t expectedType?
|
| 500 |
+
if (β logUnassignedUsingErrorInfos (β getMVars t)) then
|
| 501 |
+
throwAbortTerm
|
| 502 |
+
let t β mkAuxDefinitionFor (β mkAuxName `unsafe) t
|
| 503 |
+
let .const unsafeFn unsafeLvls .. := t.getAppFn | unreachable!
|
| 504 |
+
let .defnInfo unsafeDefn β getConstInfo unsafeFn | unreachable!
|
| 505 |
+
let implName β mkAuxName `unsafe_impl
|
| 506 |
+
addDecl <| Declaration.defnDecl {
|
| 507 |
+
name := implName
|
| 508 |
+
type := unsafeDefn.type
|
| 509 |
+
levelParams := unsafeDefn.levelParams
|
| 510 |
+
value := (β mkOfNonempty unsafeDefn.type)
|
| 511 |
+
hints := .opaque
|
| 512 |
+
safety := .safe
|
| 513 |
+
}
|
| 514 |
+
setImplementedBy implName unsafeFn
|
| 515 |
+
return mkAppN (Lean.mkConst implName unsafeLvls) t.getAppArgs
|
| 516 |
+
| _ => throwUnsupportedSyntax
|
| 517 |
+
|
| 518 |
+
/-- Elaborator for `by_elab`. -/
|
| 519 |
+
@[builtin_term_elab byElab] def elabRunElab : TermElab := fun stx expectedType? =>
|
| 520 |
+
match stx with
|
| 521 |
+
| `(by_elab $cmds:doSeq) => do
|
| 522 |
+
if let `(Lean.Parser.Term.doSeq| $e:term) := cmds then
|
| 523 |
+
if e matches `(Lean.Parser.Term.doSeq| fun $[$_args]* => $_) then
|
| 524 |
+
let tac β unsafe evalTerm
|
| 525 |
+
(Option Expr β TermElabM Expr)
|
| 526 |
+
(Lean.mkForall `x .default
|
| 527 |
+
(mkApp (Lean.mkConst ``Option) (Lean.mkConst ``Expr))
|
| 528 |
+
(mkApp (Lean.mkConst ``TermElabM) (Lean.mkConst ``Expr))) e
|
| 529 |
+
return β tac expectedType?
|
| 530 |
+
(β unsafe evalTerm (TermElabM Expr) (mkApp (Lean.mkConst ``TermElabM) (Lean.mkConst ``Expr))
|
| 531 |
+
(β `(do $cmds)))
|
| 532 |
+
| _ => throwUnsupportedSyntax
|
| 533 |
+
|
| 534 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/BuiltinTerm.lean
ADDED
|
@@ -0,0 +1,386 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Closure
|
| 8 |
+
import Lean.Meta.Diagnostics
|
| 9 |
+
import Lean.Elab.Open
|
| 10 |
+
import Lean.Elab.SetOption
|
| 11 |
+
import Lean.Elab.Eval
|
| 12 |
+
|
| 13 |
+
namespace Lean.Elab.Term
|
| 14 |
+
open Meta
|
| 15 |
+
|
| 16 |
+
@[builtin_term_elab Β«propΒ»] def elabProp : TermElab := fun _ _ =>
|
| 17 |
+
return mkSort levelZero
|
| 18 |
+
|
| 19 |
+
private def elabOptLevel (stx : Syntax) : TermElabM Level :=
|
| 20 |
+
if stx.isNone then
|
| 21 |
+
pure levelZero
|
| 22 |
+
else
|
| 23 |
+
elabLevel stx[0]
|
| 24 |
+
|
| 25 |
+
@[builtin_term_elab Β«sortΒ»] def elabSort : TermElab := fun stx _ =>
|
| 26 |
+
return mkSort (β elabOptLevel stx[1])
|
| 27 |
+
|
| 28 |
+
@[builtin_term_elab Β«typeΒ»] def elabTypeStx : TermElab := fun stx _ =>
|
| 29 |
+
return mkSort (mkLevelSucc (β elabOptLevel stx[1]))
|
| 30 |
+
|
| 31 |
+
/-!
|
| 32 |
+
the method `resolveName` adds a completion point for it using the given
|
| 33 |
+
expected type. Thus, we propagate the expected type if `stx[0]` is an identifier.
|
| 34 |
+
It doesn't "hurt" if the identifier can be resolved because the expected type is not used in this case.
|
| 35 |
+
Recall that if the name resolution fails a synthetic sorry is returned.-/
|
| 36 |
+
|
| 37 |
+
@[builtin_term_elab Β«pipeCompletionΒ»] def elabPipeCompletion : TermElab := fun stx expectedType? => do
|
| 38 |
+
let e β elabTerm stx[0] none
|
| 39 |
+
unless e.isSorry do
|
| 40 |
+
addDotCompletionInfo stx e expectedType?
|
| 41 |
+
throwErrorAt stx[1] "invalid field notation, identifier or numeral expected"
|
| 42 |
+
|
| 43 |
+
@[builtin_term_elab Β«completionΒ»] def elabCompletion : TermElab := fun stx expectedType? => do
|
| 44 |
+
/- `ident.` is ambiguous in Lean, we may try to be completing a declaration name or access a "field". -/
|
| 45 |
+
if stx[0].isIdent then
|
| 46 |
+
-- Add both an `id` and a `dot` `CompletionInfo` and have the language server figure out which
|
| 47 |
+
-- one to use.
|
| 48 |
+
addCompletionInfo <| CompletionInfo.id stx stx[0].getId (danglingDot := true) (β getLCtx) expectedType?
|
| 49 |
+
let s β saveState
|
| 50 |
+
try
|
| 51 |
+
let e β elabTerm stx[0] none
|
| 52 |
+
addDotCompletionInfo stx e expectedType?
|
| 53 |
+
catch _ =>
|
| 54 |
+
s.restore
|
| 55 |
+
throwErrorAt stx[1] "invalid field notation, identifier or numeral expected"
|
| 56 |
+
else
|
| 57 |
+
elabPipeCompletion stx expectedType?
|
| 58 |
+
|
| 59 |
+
@[builtin_term_elab Β«holeΒ»] def elabHole : TermElab := fun stx expectedType? => do
|
| 60 |
+
let kind := if (β read).inPattern || !(β read).holesAsSyntheticOpaque then MetavarKind.natural else MetavarKind.syntheticOpaque
|
| 61 |
+
let mvar β mkFreshExprMVar expectedType? kind
|
| 62 |
+
registerMVarErrorHoleInfo mvar.mvarId! stx
|
| 63 |
+
pure mvar
|
| 64 |
+
|
| 65 |
+
@[builtin_term_elab Β«syntheticHoleΒ»] def elabSyntheticHole : TermElab := fun stx expectedType? => do
|
| 66 |
+
let arg := stx[1]
|
| 67 |
+
let userName := if arg.isIdent then arg.getId else Name.anonymous
|
| 68 |
+
let mkNewHole : Unit β TermElabM Expr := fun _ => do
|
| 69 |
+
let kind := if (β read).inPattern then MetavarKind.natural else MetavarKind.syntheticOpaque
|
| 70 |
+
let mvar β mkFreshExprMVar expectedType? kind userName
|
| 71 |
+
registerMVarErrorHoleInfo mvar.mvarId! stx
|
| 72 |
+
return mvar
|
| 73 |
+
if userName.isAnonymous || (β read).inPattern then
|
| 74 |
+
mkNewHole ()
|
| 75 |
+
else
|
| 76 |
+
match (β getMCtx).findUserName? userName with
|
| 77 |
+
| none => mkNewHole ()
|
| 78 |
+
| some mvarId =>
|
| 79 |
+
let mvar := mkMVar mvarId
|
| 80 |
+
let mvarDecl β getMVarDecl mvarId
|
| 81 |
+
let lctx β getLCtx
|
| 82 |
+
if mvarDecl.lctx.isSubPrefixOf lctx then
|
| 83 |
+
return mvar
|
| 84 |
+
else match (β getExprMVarAssignment? mvarId) with
|
| 85 |
+
| some val =>
|
| 86 |
+
let val β instantiateMVars val
|
| 87 |
+
if (β MetavarContext.isWellFormed lctx val) then
|
| 88 |
+
return val
|
| 89 |
+
else
|
| 90 |
+
withLCtx mvarDecl.lctx mvarDecl.localInstances do
|
| 91 |
+
throwError "synthetic hole has already been defined and assigned to value incompatible with the current context{indentExpr val}"
|
| 92 |
+
| none =>
|
| 93 |
+
if (β mvarId.isDelayedAssigned) then
|
| 94 |
+
-- We can try to improve this case if needed.
|
| 95 |
+
throwError "synthetic hole has already beend defined and delayed assigned with an incompatible local context"
|
| 96 |
+
else if lctx.isSubPrefixOf mvarDecl.lctx then
|
| 97 |
+
let mvarNew β mkNewHole ()
|
| 98 |
+
mvarId.assign mvarNew
|
| 99 |
+
return mvarNew
|
| 100 |
+
else
|
| 101 |
+
throwError "synthetic hole has already been defined with an incompatible local context"
|
| 102 |
+
|
| 103 |
+
@[builtin_term_elab Lean.Parser.Term.omission] def elabOmission : TermElab := fun stx expectedType? => do
|
| 104 |
+
logWarning m!"\
|
| 105 |
+
The 'β―' token is used by the pretty printer to indicate omitted terms, and it should not be used directly. \
|
| 106 |
+
It logs this warning and then elaborates like '_'.\
|
| 107 |
+
\n\n\
|
| 108 |
+
The presence of 'β―' in pretty printing output is controlled by the 'pp.maxSteps', 'pp.deepTerms' and 'pp.proofs' options. \
|
| 109 |
+
These options can be further adjusted using 'pp.deepTerms.threshold' and 'pp.proofs.threshold'. \
|
| 110 |
+
If this 'β―' was copied from the Infoview, the hover there for the original 'β―' explains which of these options led to the omission."
|
| 111 |
+
elabHole stx expectedType?
|
| 112 |
+
|
| 113 |
+
@[builtin_term_elab Β«letMVarΒ»] def elabLetMVar : TermElab := fun stx expectedType? => do
|
| 114 |
+
match stx with
|
| 115 |
+
| `(let_mvar% ? $n := $e; $b) =>
|
| 116 |
+
match (β getMCtx).findUserName? n.getId with
|
| 117 |
+
| some _ => throwError "invalid 'let_mvar%', metavariable '?{n.getId}' has already been used"
|
| 118 |
+
| none =>
|
| 119 |
+
let e β elabTerm e none
|
| 120 |
+
let mvar β mkFreshExprMVar (β inferType e) MetavarKind.syntheticOpaque n.getId
|
| 121 |
+
mvar.mvarId!.assign e
|
| 122 |
+
-- We use `mkSaveInfoAnnotation` to make sure the info trees for `e` are saved even if `b` is a metavariable.
|
| 123 |
+
return mkSaveInfoAnnotation (β elabTerm b expectedType?)
|
| 124 |
+
| _ => throwUnsupportedSyntax
|
| 125 |
+
|
| 126 |
+
private def getMVarFromUserName (ident : Syntax) : MetaM Expr := do
|
| 127 |
+
match (β getMCtx).findUserName? ident.getId with
|
| 128 |
+
| none => throwError "unknown metavariable '?{ident.getId}'"
|
| 129 |
+
| some mvarId => instantiateMVars (mkMVar mvarId)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
@[builtin_term_elab Β«waitIfTypeMVarΒ»] def elabWaitIfTypeMVar : TermElab := fun stx expectedType? => do
|
| 133 |
+
match stx with
|
| 134 |
+
| `(wait_if_type_mvar% ? $n; $b) =>
|
| 135 |
+
tryPostponeIfMVar (β inferType (β getMVarFromUserName n))
|
| 136 |
+
elabTerm b expectedType?
|
| 137 |
+
| _ => throwUnsupportedSyntax
|
| 138 |
+
|
| 139 |
+
@[builtin_term_elab Β«waitIfTypeContainsMVarΒ»] def elabWaitIfTypeContainsMVar : TermElab := fun stx expectedType? => do
|
| 140 |
+
match stx with
|
| 141 |
+
| `(wait_if_type_contains_mvar% ? $n; $b) =>
|
| 142 |
+
if (β instantiateMVars (β inferType (β getMVarFromUserName n))).hasExprMVar then
|
| 143 |
+
tryPostpone
|
| 144 |
+
elabTerm b expectedType?
|
| 145 |
+
| _ => throwUnsupportedSyntax
|
| 146 |
+
|
| 147 |
+
@[builtin_term_elab Β«waitIfContainsMVarΒ»] def elabWaitIfContainsMVar : TermElab := fun stx expectedType? => do
|
| 148 |
+
match stx with
|
| 149 |
+
| `(wait_if_contains_mvar% ? $n; $b) =>
|
| 150 |
+
if (β getMVarFromUserName n).hasExprMVar then
|
| 151 |
+
tryPostpone
|
| 152 |
+
elabTerm b expectedType?
|
| 153 |
+
| _ => throwUnsupportedSyntax
|
| 154 |
+
|
| 155 |
+
@[builtin_term_elab byTactic] def elabByTactic : TermElab := fun stx expectedType? => do
|
| 156 |
+
match expectedType? with
|
| 157 |
+
| some expectedType =>
|
| 158 |
+
-- `by` switches from an exported to a private context, so we must disallow unassigned
|
| 159 |
+
-- metavariables in the goal in this case as they could otherwise leak private data back into
|
| 160 |
+
-- the exported context.
|
| 161 |
+
mkTacticMVar expectedType stx .term (delayOnMVars := (β getEnv).isExporting)
|
| 162 |
+
| none =>
|
| 163 |
+
tryPostpone
|
| 164 |
+
throwError ("invalid 'by' tactic, expected type has not been provided")
|
| 165 |
+
|
| 166 |
+
@[builtin_term_elab noImplicitLambda] def elabNoImplicitLambda : TermElab := fun stx expectedType? =>
|
| 167 |
+
elabTerm stx[1] (mkNoImplicitLambdaAnnotation <$> expectedType?)
|
| 168 |
+
|
| 169 |
+
@[builtin_term_elab Lean.Parser.Term.cdot] def elabBadCDot : TermElab := fun stx expectedType? => do
|
| 170 |
+
if stx[0].getAtomVal == "." then
|
| 171 |
+
-- Users may input bad cdots because they are trying to auto-complete them using the expected type
|
| 172 |
+
addCompletionInfo <| CompletionInfo.dotId stx .anonymous (β getLCtx) expectedType?
|
| 173 |
+
throwError "invalid occurrence of `Β·` notation, it must be surrounded by parentheses (e.g. `(Β· + 1)`)"
|
| 174 |
+
|
| 175 |
+
@[builtin_term_elab str] def elabStrLit : TermElab := fun stx _ => do
|
| 176 |
+
match stx.isStrLit? with
|
| 177 |
+
| some val => pure $ mkStrLit val
|
| 178 |
+
| none => throwIllFormedSyntax
|
| 179 |
+
|
| 180 |
+
private def mkFreshTypeMVarFor (expectedType? : Option Expr) : TermElabM Expr := do
|
| 181 |
+
let typeMVar β mkFreshTypeMVar MetavarKind.synthetic
|
| 182 |
+
match expectedType? with
|
| 183 |
+
| some expectedType => discard <| isDefEq expectedType typeMVar
|
| 184 |
+
| _ => pure ()
|
| 185 |
+
return typeMVar
|
| 186 |
+
|
| 187 |
+
@[builtin_term_elab num] def elabNumLit : TermElab := fun stx expectedType? => do
|
| 188 |
+
let val β match stx.isNatLit? with
|
| 189 |
+
| some val => pure val
|
| 190 |
+
| none => throwIllFormedSyntax
|
| 191 |
+
let typeMVar β mkFreshTypeMVarFor expectedType?
|
| 192 |
+
let u β try
|
| 193 |
+
getDecLevel typeMVar
|
| 194 |
+
catch ex =>
|
| 195 |
+
match expectedType? with
|
| 196 |
+
| some expectedType =>
|
| 197 |
+
if (β isProp expectedType) then
|
| 198 |
+
throwError m!"numerals are data in Lean, but the expected type is a proposition{indentExpr expectedType} : Prop"
|
| 199 |
+
else
|
| 200 |
+
throwError m!"numerals are data in Lean, but the expected type is universe polymorphic and may be a proposition{indentExpr expectedType} : {β inferType expectedType}"
|
| 201 |
+
| none => throw ex
|
| 202 |
+
let extraMsg := m!"numerals are polymorphic in Lean, but the numeral `{val}` cannot be used in a context where the expected type is{indentExpr typeMVar}\ndue to the absence of the instance above"
|
| 203 |
+
let mvar β mkInstMVar (mkApp2 (Lean.mkConst ``OfNat [u]) typeMVar (mkRawNatLit val)) extraMsg
|
| 204 |
+
let r := mkApp3 (Lean.mkConst ``OfNat.ofNat [u]) typeMVar (mkRawNatLit val) mvar
|
| 205 |
+
registerMVarErrorImplicitArgInfo mvar.mvarId! stx r
|
| 206 |
+
return r
|
| 207 |
+
|
| 208 |
+
@[builtin_term_elab rawNatLit] def elabRawNatLit : TermElab := fun stx _ => do
|
| 209 |
+
match stx[1].isNatLit? with
|
| 210 |
+
| some val => return mkRawNatLit val
|
| 211 |
+
| none => throwIllFormedSyntax
|
| 212 |
+
|
| 213 |
+
@[builtin_term_elab scientific]
|
| 214 |
+
def elabScientificLit : TermElab := fun stx expectedType? => do
|
| 215 |
+
match stx.isScientificLit? with
|
| 216 |
+
| none => throwIllFormedSyntax
|
| 217 |
+
| some (m, sign, e) =>
|
| 218 |
+
let typeMVar β mkFreshTypeMVarFor expectedType?
|
| 219 |
+
let u β getDecLevel typeMVar
|
| 220 |
+
let mvar β mkInstMVar (mkApp (Lean.mkConst ``OfScientific [u]) typeMVar)
|
| 221 |
+
let r := mkApp5 (Lean.mkConst ``OfScientific.ofScientific [u]) typeMVar mvar (mkRawNatLit m) (toExpr sign) (mkRawNatLit e)
|
| 222 |
+
registerMVarErrorImplicitArgInfo mvar.mvarId! stx r
|
| 223 |
+
return r
|
| 224 |
+
|
| 225 |
+
@[builtin_term_elab char] def elabCharLit : TermElab := fun stx _ => do
|
| 226 |
+
match stx.isCharLit? with
|
| 227 |
+
| some val => return mkApp (Lean.mkConst ``Char.ofNat) (mkRawNatLit val.toNat)
|
| 228 |
+
| none => throwIllFormedSyntax
|
| 229 |
+
|
| 230 |
+
@[builtin_term_elab quotedName] def elabQuotedName : TermElab := fun stx _ =>
|
| 231 |
+
match stx[0].isNameLit? with
|
| 232 |
+
| some val => pure $ toExpr val
|
| 233 |
+
| none => throwIllFormedSyntax
|
| 234 |
+
|
| 235 |
+
@[builtin_term_elab doubleQuotedName] def elabDoubleQuotedName : TermElab := fun stx _ =>
|
| 236 |
+
return toExpr (β realizeGlobalConstNoOverloadWithInfo stx[2])
|
| 237 |
+
|
| 238 |
+
@[builtin_term_elab declName] def elabDeclName : TermElab := adaptExpander fun _ => do
|
| 239 |
+
let some declName β getDeclName?
|
| 240 |
+
| throwError "invalid `decl_name%` macro, the declaration name is not available"
|
| 241 |
+
return (quote declName : Term)
|
| 242 |
+
|
| 243 |
+
@[builtin_term_elab Parser.Term.withDeclName] def elabWithDeclName : TermElab := fun stx expectedType? => do
|
| 244 |
+
let id := stx[2].getId
|
| 245 |
+
let id β if stx[1].isNone then pure id else pure <| (β getCurrNamespace) ++ id
|
| 246 |
+
let e := stx[3]
|
| 247 |
+
withMacroExpansion stx e <| withDeclName id <| elabTerm e expectedType?
|
| 248 |
+
|
| 249 |
+
@[builtin_term_elab typeOf] def elabTypeOf : TermElab := fun stx _ => do
|
| 250 |
+
inferType (β elabTerm stx[1] none)
|
| 251 |
+
|
| 252 |
+
/--
|
| 253 |
+
Recall that `mkTermInfo` does not create an `ofTermInfo` node in the info tree
|
| 254 |
+
if `e` corresponds to a hole that is going to be filled "later" by executing a tactic or resuming elaboration.
|
| 255 |
+
This behavior is problematic for auxiliary elaboration steps that are "almost" no-ops.
|
| 256 |
+
For example, consider the elaborator for
|
| 257 |
+
```
|
| 258 |
+
ensure_type_of% s msg e
|
| 259 |
+
```
|
| 260 |
+
It elaborates `s`, infers its type `t`, and then elaborates `e` ensuring the resulting type is `t`.
|
| 261 |
+
If the elaboration of `e` is postponed, then the result is just a metavariable, and an `ofTermInfo` would not be created.
|
| 262 |
+
This happens because `ensure_type_of%` is almost a no-op. The elaboration of `s` does not directly contribute to the
|
| 263 |
+
final result, just its type.
|
| 264 |
+
To make sure, we don't miss any information in the `InfoTree`, we can just create a "silent" annotation to force
|
| 265 |
+
`mTermInfo` to create a node for the `ensure_type_of% s msg e` even if `e` has been postponed.
|
| 266 |
+
|
| 267 |
+
Another possible solution is to elaborate `ensure_type_of% s msg e` as `ensureType s e` where `ensureType` has type
|
| 268 |
+
```
|
| 269 |
+
ensureType (s e : Ξ±) := e
|
| 270 |
+
```
|
| 271 |
+
We decided to use the silent notation because `ensure_type_of%` is heavily used in the `Do` elaborator, and the extra
|
| 272 |
+
overhead could be significant.
|
| 273 |
+
-/
|
| 274 |
+
private def mkSilentAnnotationIfHole (e : Expr) : TermElabM Expr := do
|
| 275 |
+
if (β isTacticOrPostponedHole? e).isSome then
|
| 276 |
+
return mkAnnotation `_silent e
|
| 277 |
+
else
|
| 278 |
+
return e
|
| 279 |
+
|
| 280 |
+
@[builtin_term_elab ensureTypeOf] def elabEnsureTypeOf : TermElab := fun stx _ =>
|
| 281 |
+
match stx[2].isStrLit? with
|
| 282 |
+
| none => throwIllFormedSyntax
|
| 283 |
+
| some msg => do
|
| 284 |
+
let refTerm β elabTerm stx[1] none
|
| 285 |
+
let refTermType β inferType refTerm
|
| 286 |
+
-- See comment at `mkSilentAnnotationIfHole`
|
| 287 |
+
mkSilentAnnotationIfHole (β elabTermEnsuringType stx[3] refTermType (errorMsgHeader? := msg))
|
| 288 |
+
|
| 289 |
+
@[builtin_term_elab ensureExpectedType] def elabEnsureExpectedType : TermElab := fun stx expectedType? =>
|
| 290 |
+
match stx[1].isStrLit? with
|
| 291 |
+
| none => throwIllFormedSyntax
|
| 292 |
+
| some msg => elabTermEnsuringType stx[2] expectedType? (errorMsgHeader? := msg)
|
| 293 |
+
|
| 294 |
+
@[builtin_term_elab valueOf] def elabValueOf : TermElab := fun stx _ => do
|
| 295 |
+
let ident := stx[1]
|
| 296 |
+
let some e β Term.resolveId? stx[1] (withInfo := true)
|
| 297 |
+
| throwUnknownConstantAt ident ident.getId
|
| 298 |
+
match e with
|
| 299 |
+
| .const c us => do
|
| 300 |
+
let cinfo β getConstInfo c
|
| 301 |
+
unless cinfo.hasValue do throwErrorAt ident "Constant has no value."
|
| 302 |
+
return cinfo.instantiateValueLevelParams! us
|
| 303 |
+
| .fvar fvarId => do
|
| 304 |
+
let some val β fvarId.getValue? | throwErrorAt ident "Local declaration has no value."
|
| 305 |
+
return val
|
| 306 |
+
| _ => panic! "resolveId? returned an unexpected expression"
|
| 307 |
+
|
| 308 |
+
@[builtin_term_elab clear] def elabClear : TermElab := fun stx expectedType? => do
|
| 309 |
+
let some (.fvar fvarId) β isLocalIdent? stx[1]
|
| 310 |
+
| throwErrorAt stx[1] "not in scope"
|
| 311 |
+
let body := stx[3]
|
| 312 |
+
let canClear β id do
|
| 313 |
+
if let some expectedType := expectedType? then
|
| 314 |
+
if β dependsOn expectedType fvarId then
|
| 315 |
+
return false
|
| 316 |
+
for ldecl in β getLCtx do
|
| 317 |
+
if ldecl.fvarId != fvarId then
|
| 318 |
+
if β localDeclDependsOn ldecl fvarId then
|
| 319 |
+
return false
|
| 320 |
+
return true
|
| 321 |
+
if canClear then
|
| 322 |
+
withErasedFVars #[fvarId] do elabTerm body expectedType?
|
| 323 |
+
else
|
| 324 |
+
elabTerm body expectedType?
|
| 325 |
+
|
| 326 |
+
@[builtin_term_elab Β«openΒ»] def elabOpen : TermElab := fun stx expectedType? => do
|
| 327 |
+
let `(open $decl in $e) := stx | throwUnsupportedSyntax
|
| 328 |
+
try
|
| 329 |
+
pushScope
|
| 330 |
+
let openDecls β elabOpenDecl decl
|
| 331 |
+
withTheReader Core.Context (fun ctx => { ctx with openDecls := openDecls }) do
|
| 332 |
+
elabTerm e expectedType?
|
| 333 |
+
finally
|
| 334 |
+
popScope
|
| 335 |
+
|
| 336 |
+
@[builtin_term_elab Β«set_optionΒ»] def elabSetOption : TermElab := fun stx expectedType? => do
|
| 337 |
+
let options β Elab.elabSetOption stx[1] stx[3]
|
| 338 |
+
withOptions (fun _ => options) do
|
| 339 |
+
try
|
| 340 |
+
elabTerm stx[5] expectedType?
|
| 341 |
+
finally
|
| 342 |
+
if stx[1].getId == `diagnostics then
|
| 343 |
+
reportDiag
|
| 344 |
+
|
| 345 |
+
@[builtin_term_elab withAnnotateTerm] def elabWithAnnotateTerm : TermElab := fun stx expectedType? => do
|
| 346 |
+
match stx with
|
| 347 |
+
| `(with_annotate_term $stx $e) =>
|
| 348 |
+
withTermInfoContext' .anonymous stx (expectedType? := expectedType?) (elabTerm e expectedType?)
|
| 349 |
+
| _ => throwUnsupportedSyntax
|
| 350 |
+
|
| 351 |
+
private unsafe def evalFilePathUnsafe (stx : Syntax) : TermElabM System.FilePath :=
|
| 352 |
+
evalTerm System.FilePath (Lean.mkConst ``System.FilePath) stx
|
| 353 |
+
|
| 354 |
+
@[implemented_by evalFilePathUnsafe]
|
| 355 |
+
private opaque evalFilePath (stx : Syntax) : TermElabM System.FilePath
|
| 356 |
+
|
| 357 |
+
@[builtin_term_elab includeStr] def elabIncludeStr : TermElab
|
| 358 |
+
| `(include_str $path:term), _ => do
|
| 359 |
+
let path β evalFilePath path
|
| 360 |
+
let ctx β readThe Lean.Core.Context
|
| 361 |
+
let srcPath := System.FilePath.mk ctx.fileName
|
| 362 |
+
let some srcDir := srcPath.parent
|
| 363 |
+
| throwError "cannot compute parent directory of '{srcPath}'"
|
| 364 |
+
let path := srcDir / path
|
| 365 |
+
mkStrLit <$> IO.FS.readFile path
|
| 366 |
+
| _, _ => throwUnsupportedSyntax
|
| 367 |
+
|
| 368 |
+
@[builtin_term_elab Lean.Parser.Term.namedPattern] def elabNamedPatternErr : TermElab := fun stx _ =>
|
| 369 |
+
throwError "`<identifier>@<term>` is a named pattern and can only be used in pattern matching contexts{indentD stx}"
|
| 370 |
+
|
| 371 |
+
@[builtin_term_elab Β«privateDeclΒ»] def elabPrivateDecl : TermElab := fun stx expectedType? => do
|
| 372 |
+
match stx with
|
| 373 |
+
| `(Parser.Term.privateDecl| private_decl% $e) =>
|
| 374 |
+
if (β getEnv).isExporting then
|
| 375 |
+
let name β mkAuxDeclName `_private
|
| 376 |
+
withoutExporting do
|
| 377 |
+
let e β elabTerm e expectedType?
|
| 378 |
+
-- Inline as changing visibility should not affect run time.
|
| 379 |
+
-- Eventually we would like to be more conscious about inlining of instance fields,
|
| 380 |
+
-- irrespective of `private` use.
|
| 381 |
+
mkAuxDefinitionFor name e <* setInlineAttribute name
|
| 382 |
+
else
|
| 383 |
+
elabTerm e expectedType?
|
| 384 |
+
| _ => throwUnsupportedSyntax
|
| 385 |
+
|
| 386 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Calc.lean
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.App
|
| 8 |
+
|
| 9 |
+
namespace Lean.Elab.Term
|
| 10 |
+
open Meta
|
| 11 |
+
|
| 12 |
+
/--
|
| 13 |
+
Decompose `e` into `(r, a, b)`.
|
| 14 |
+
|
| 15 |
+
Remark: it assumes the last two arguments are explicit.
|
| 16 |
+
-/
|
| 17 |
+
def getCalcRelation? (e : Expr) : MetaM (Option (Expr Γ Expr Γ Expr)) := do
|
| 18 |
+
if e.getAppNumArgs < 2 then
|
| 19 |
+
return none
|
| 20 |
+
else
|
| 21 |
+
return some (e.appFn!.appFn!, e.appFn!.appArg!, e.appArg!)
|
| 22 |
+
|
| 23 |
+
private def getRelUniv (r : Expr) : MetaM Level := do
|
| 24 |
+
let rType β inferType r
|
| 25 |
+
forallTelescopeReducing rType fun _ sort => do
|
| 26 |
+
let .sort u β whnf sort | throwError "unexpected relation type{indentExpr rType}"
|
| 27 |
+
return u
|
| 28 |
+
|
| 29 |
+
def mkCalcTrans (result resultType step stepType : Expr) : MetaM (Expr Γ Expr) := do
|
| 30 |
+
let some (r, a, b) β getCalcRelation? resultType | unreachable!
|
| 31 |
+
let some (s, _, c) β getCalcRelation? (β instantiateMVars stepType) | unreachable!
|
| 32 |
+
let u β getRelUniv r
|
| 33 |
+
let v β getRelUniv s
|
| 34 |
+
let (Ξ±, Ξ², Ξ³) := (β inferType a, β inferType b, β inferType c)
|
| 35 |
+
let (u_1, u_2, u_3) := (β getLevel Ξ±, β getLevel Ξ², β getLevel Ξ³)
|
| 36 |
+
let w β mkFreshLevelMVar
|
| 37 |
+
let t β mkFreshExprMVar (β mkArrow Ξ± (β mkArrow Ξ³ (mkSort w)))
|
| 38 |
+
let selfType := mkAppN (Lean.mkConst ``Trans [u, v, w, u_1, u_2, u_3]) #[Ξ±, Ξ², Ξ³, r, s, t]
|
| 39 |
+
match (β trySynthInstance selfType) with
|
| 40 |
+
| .some self =>
|
| 41 |
+
let result := mkAppN (Lean.mkConst ``Trans.trans [u, v, w, u_1, u_2, u_3]) #[Ξ±, Ξ², Ξ³, r, s, t, self, a, b, c, result, step]
|
| 42 |
+
let resultType := (β instantiateMVars (β inferType result)).headBeta
|
| 43 |
+
unless (β getCalcRelation? resultType).isSome do
|
| 44 |
+
throwError "invalid 'calc' step, step result is not a relation{indentExpr resultType}"
|
| 45 |
+
return (result, resultType)
|
| 46 |
+
| _ => throwError "invalid 'calc' step, failed to synthesize `Trans` instance{indentExpr selfType}{useDiagnosticMsg}"
|
| 47 |
+
|
| 48 |
+
/--
|
| 49 |
+
Adds a type annotation to a hole that occurs immediately at the beginning of the term.
|
| 50 |
+
This is so that coercions can trigger when elaborating the term.
|
| 51 |
+
See https://github.com/leanprover/lean4/issues/2040 for further rationale.
|
| 52 |
+
|
| 53 |
+
- `_ < 3` is annotated
|
| 54 |
+
- `(_) < 3` is not, because it occurs after an atom
|
| 55 |
+
- in `_ < _` only the first one is annotated
|
| 56 |
+
- `_ + 2 < 3` is annotated (not the best heuristic, ideally we'd like to annotate `_ + 2`)
|
| 57 |
+
- `lt _ 3` is not, because it occurs after an identifier
|
| 58 |
+
-/
|
| 59 |
+
partial def annotateFirstHoleWithType (t : Term) (type : Expr) : TermElabM Term :=
|
| 60 |
+
-- The state is true if we should annotate the immediately next hole with the type.
|
| 61 |
+
return β¨β StateT.run' (go t) trueβ©
|
| 62 |
+
where
|
| 63 |
+
go (t : Syntax) := do
|
| 64 |
+
unless β get do return t
|
| 65 |
+
match t with
|
| 66 |
+
| .node _ ``Lean.Parser.Term.hole _ =>
|
| 67 |
+
set false
|
| 68 |
+
`(($(β¨tβ©) : $(β exprToSyntax type)))
|
| 69 |
+
| .node i k as => return .node i k (β as.mapM go)
|
| 70 |
+
| _ => set false; return t
|
| 71 |
+
|
| 72 |
+
/-- View of a `calcStep`. -/
|
| 73 |
+
structure CalcStepView where
|
| 74 |
+
ref : Syntax
|
| 75 |
+
/-- A relation term like `a β€ b` -/
|
| 76 |
+
term : Term
|
| 77 |
+
/-- A proof of `term` -/
|
| 78 |
+
proof : Term
|
| 79 |
+
deriving Inhabited
|
| 80 |
+
|
| 81 |
+
def mkCalcFirstStepView (step0 : TSyntax ``calcFirstStep) : TermElabM CalcStepView :=
|
| 82 |
+
withRef step0 do
|
| 83 |
+
match step0 with
|
| 84 |
+
| `(calcFirstStep| $term:term) => return { ref := step0, term := β `($term = _), proof := β ``(rfl)}
|
| 85 |
+
| `(calcFirstStep| $term := $proof) => return { ref := step0, term, proof}
|
| 86 |
+
| _ => throwUnsupportedSyntax
|
| 87 |
+
|
| 88 |
+
def mkCalcStepViews (steps : TSyntax ``calcSteps) : TermElabM (Array CalcStepView) :=
|
| 89 |
+
match steps with
|
| 90 |
+
| `(calcSteps|
|
| 91 |
+
$step0:calcFirstStep
|
| 92 |
+
$rest*) => do
|
| 93 |
+
let mut steps := #[β mkCalcFirstStepView step0]
|
| 94 |
+
for step in rest do
|
| 95 |
+
let `(calcStep| $term := $proof) := step | throwUnsupportedSyntax
|
| 96 |
+
steps := steps.push { ref := step, term, proof }
|
| 97 |
+
return steps
|
| 98 |
+
| _ => throwUnsupportedSyntax
|
| 99 |
+
|
| 100 |
+
def elabCalcSteps (steps : Array CalcStepView) : TermElabM (Expr Γ Expr) := do
|
| 101 |
+
let mut result? := none
|
| 102 |
+
let mut prevRhs? := none
|
| 103 |
+
for step in steps do
|
| 104 |
+
let type β elabType <| β do
|
| 105 |
+
if let some prevRhs := prevRhs? then
|
| 106 |
+
annotateFirstHoleWithType step.term (β inferType prevRhs)
|
| 107 |
+
else
|
| 108 |
+
pure step.term
|
| 109 |
+
let some (_, lhs, rhs) β getCalcRelation? type |
|
| 110 |
+
throwErrorAt step.term "invalid 'calc' step, relation expected{indentExpr type}"
|
| 111 |
+
if let some prevRhs := prevRhs? then
|
| 112 |
+
unless (β isDefEqGuarded lhs prevRhs) do
|
| 113 |
+
throwErrorAt step.term "\
|
| 114 |
+
invalid 'calc' step, left-hand side is{indentD m!"{lhs} : {β inferType lhs}"}\n\
|
| 115 |
+
but previous right-hand side is{indentD m!"{prevRhs} : {β inferType prevRhs}"}"
|
| 116 |
+
let proof β withFreshMacroScope do elabTermEnsuringType step.proof type
|
| 117 |
+
result? := some <| β do
|
| 118 |
+
if let some (result, resultType) := result? then
|
| 119 |
+
synthesizeSyntheticMVarsUsingDefault
|
| 120 |
+
withRef step.term do mkCalcTrans result resultType proof type
|
| 121 |
+
else
|
| 122 |
+
pure (proof, type)
|
| 123 |
+
prevRhs? := rhs
|
| 124 |
+
synthesizeSyntheticMVarsUsingDefault
|
| 125 |
+
return result?.get!
|
| 126 |
+
|
| 127 |
+
def throwCalcFailure (steps : Array CalcStepView) (expectedType result : Expr) : MetaM Ξ± := do
|
| 128 |
+
let resultType := (β instantiateMVars (β inferType result)).headBeta
|
| 129 |
+
let some (r, lhs, rhs) β getCalcRelation? resultType | unreachable!
|
| 130 |
+
if let some (er, elhs, erhs) β getCalcRelation? expectedType then
|
| 131 |
+
if β isDefEqGuarded r er then
|
| 132 |
+
let mut failed := false
|
| 133 |
+
unless β isDefEqGuarded lhs elhs do
|
| 134 |
+
let (lhs, elhs) β addPPExplicitToExposeDiff lhs elhs
|
| 135 |
+
let (lhsTy, elhsTy) β addPPExplicitToExposeDiff (β inferType lhs) (β inferType elhs)
|
| 136 |
+
logErrorAt steps[0]!.term m!"\
|
| 137 |
+
invalid 'calc' step, left-hand side is{indentD m!"{lhs} : {lhsTy}"}\n\
|
| 138 |
+
but is expected to be{indentD m!"{elhs} : {elhsTy}"}"
|
| 139 |
+
failed := true
|
| 140 |
+
unless β isDefEqGuarded rhs erhs do
|
| 141 |
+
let (rhs, erhs) β addPPExplicitToExposeDiff rhs erhs
|
| 142 |
+
let (rhsTy, erhsTy) β addPPExplicitToExposeDiff (β inferType rhs) (β inferType erhs)
|
| 143 |
+
logErrorAt steps.back!.term m!"\
|
| 144 |
+
invalid 'calc' step, right-hand side is{indentD m!"{rhs} : {rhsTy}"}\n\
|
| 145 |
+
but is expected to be{indentD m!"{erhs} : {erhsTy}"}"
|
| 146 |
+
failed := true
|
| 147 |
+
if failed then
|
| 148 |
+
throwAbortTerm
|
| 149 |
+
throwTypeMismatchError "'calc' expression" expectedType resultType result
|
| 150 |
+
|
| 151 |
+
/-!
|
| 152 |
+
Warning! It is *very* tempting to try to improve `calc` so that it makes use of the expected type
|
| 153 |
+
to unify with the LHS and RHS.
|
| 154 |
+
Two people have already re-implemented `elabCalcSteps` trying to do so and then reverted the changes,
|
| 155 |
+
not being aware of examples like https://github.com/leanprover/lean4/issues/2073
|
| 156 |
+
|
| 157 |
+
The problem is that the expected type might need to be unfolded to get an accurate LHS and RHS.
|
| 158 |
+
(Consider `β€` vs `β₯`. Users expect to be able to use `calc` to prove `β₯` using chained `β€`!)
|
| 159 |
+
Furthermore, the types of the LHS and RHS do not need to be the same (consider `x β S` as a relation),
|
| 160 |
+
so we also cannot use the expected LHS and RHS as type hints.
|
| 161 |
+
-/
|
| 162 |
+
|
| 163 |
+
/-- Elaborator for the `calc` term mode variant. -/
|
| 164 |
+
@[builtin_term_elab Lean.calc]
|
| 165 |
+
def elabCalc : TermElab
|
| 166 |
+
| `(calc%$tk $steps:calcSteps), expectedType? => withRef tk do
|
| 167 |
+
let steps β mkCalcStepViews steps
|
| 168 |
+
let (result, _) β elabCalcSteps steps
|
| 169 |
+
ensureHasTypeWithErrorMsgs expectedType? result
|
| 170 |
+
(mkImmedErrorMsg := fun _ => throwCalcFailure steps)
|
| 171 |
+
(mkErrorMsg := fun _ => throwCalcFailure steps)
|
| 172 |
+
| _, _ => throwUnsupportedSyntax
|
| 173 |
+
|
| 174 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/CheckTactic.lean
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Joe Hendrix
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Tactic.ElabTerm
|
| 8 |
+
import Lean.Elab.Command
|
| 9 |
+
import Lean.Elab.Tactic.Meta
|
| 10 |
+
import Lean.Meta.CheckTactic
|
| 11 |
+
|
| 12 |
+
/-!
|
| 13 |
+
Commands to validate tactic results.
|
| 14 |
+
-/
|
| 15 |
+
|
| 16 |
+
namespace Lean.Elab.CheckTactic
|
| 17 |
+
|
| 18 |
+
open Lean.Meta CheckTactic
|
| 19 |
+
open Lean.Elab.Tactic
|
| 20 |
+
open Lean.Elab.Term
|
| 21 |
+
open Lean.Elab.Command
|
| 22 |
+
|
| 23 |
+
@[builtin_command_elab Lean.Parser.checkTactic]
|
| 24 |
+
def elabCheckTactic : CommandElab := fun stx => do
|
| 25 |
+
let `(#check_tactic $t ~> $result by $tac) := stx | throwUnsupportedSyntax
|
| 26 |
+
withoutModifyingEnv $ do
|
| 27 |
+
runTermElabM $ fun _vars => do
|
| 28 |
+
let u β withSynthesize (postpone := .no) <| Lean.Elab.Term.elabTerm t none
|
| 29 |
+
let type β inferType u
|
| 30 |
+
let checkGoalType β mkCheckGoalType u type
|
| 31 |
+
let mvar β mkFreshExprMVar (.some checkGoalType)
|
| 32 |
+
let expTerm β Lean.Elab.Term.elabTerm result (.some type)
|
| 33 |
+
let (goals, _) β Lean.Elab.runTactic mvar.mvarId! tac.raw
|
| 34 |
+
match goals with
|
| 35 |
+
| [] =>
|
| 36 |
+
throwErrorAt stx
|
| 37 |
+
m!"{tac} closed goal, but is expected to reduce to {indentExpr expTerm}."
|
| 38 |
+
| [next] => do
|
| 39 |
+
let (val, _, _) β matchCheckGoalType stx (βnext.getType)
|
| 40 |
+
if !(β Meta.withReducible <| isDefEq val expTerm) then
|
| 41 |
+
let (val, expTerm) β addPPExplicitToExposeDiff val expTerm
|
| 42 |
+
throwErrorAt stx
|
| 43 |
+
m!"Term reduces to{indentExpr val}\nbut is expected to reduce to {indentExpr expTerm}"
|
| 44 |
+
| _ => do
|
| 45 |
+
throwErrorAt stx
|
| 46 |
+
m!"{tac} produced multiple goals, but is expected to reduce to {indentExpr expTerm}."
|
| 47 |
+
|
| 48 |
+
@[builtin_command_elab Lean.Parser.checkTacticFailure]
|
| 49 |
+
def elabCheckTacticFailure : CommandElab := fun stx => do
|
| 50 |
+
let `(#check_tactic_failure $t by $tactic) := stx | throwUnsupportedSyntax
|
| 51 |
+
withoutModifyingEnv $ do
|
| 52 |
+
runTermElabM $ fun _vars => do
|
| 53 |
+
let val β Lean.Elab.Term.elabTerm t none
|
| 54 |
+
let type β inferType val
|
| 55 |
+
let checkGoalType β mkCheckGoalType val type
|
| 56 |
+
let mvar β mkFreshExprMVar (.some checkGoalType)
|
| 57 |
+
let act := Lean.Elab.runTactic mvar.mvarId! tactic.raw
|
| 58 |
+
match β try (Term.withoutErrToSorry (some <$> act)) catch _ => pure none with
|
| 59 |
+
| none =>
|
| 60 |
+
pure ()
|
| 61 |
+
| some (gls, _) =>
|
| 62 |
+
let ppGoal (g : MVarId) := do
|
| 63 |
+
let (val, _type, _u) β matchCheckGoalType stx (β g.getType)
|
| 64 |
+
pure m!"{indentExpr val}"
|
| 65 |
+
let msg β
|
| 66 |
+
match gls with
|
| 67 |
+
| [] => pure m!"{tactic} expected to fail on {t}, but closed goal."
|
| 68 |
+
| [g] =>
|
| 69 |
+
pure <| m!"{tactic} expected to fail on {t}, but returned: {βppGoal g}"
|
| 70 |
+
| gls =>
|
| 71 |
+
let app m g := do pure <| m ++ (βppGoal g)
|
| 72 |
+
let init := m!"{tactic} expected to fail on {t}, but returned goals:"
|
| 73 |
+
gls.foldlM (init := init) app
|
| 74 |
+
throwErrorAt stx msg
|
| 75 |
+
|
| 76 |
+
@[builtin_macro Lean.Parser.checkSimp]
|
| 77 |
+
def expandCheckSimp : Macro := fun stx => do
|
| 78 |
+
let `(#check_simp $t ~> $exp) := stx | Macro.throwUnsupported
|
| 79 |
+
`(command|#check_tactic $t ~> $exp by simp)
|
| 80 |
+
|
| 81 |
+
@[builtin_macro Lean.Parser.checkSimpFailure]
|
| 82 |
+
def expandCheckSimpFailure : Macro := fun stx => do
|
| 83 |
+
let `(#check_simp $t !~>) := stx | Macro.throwUnsupported
|
| 84 |
+
`(command|#check_tactic_failure $t by simp)
|
| 85 |
+
|
| 86 |
+
end Lean.Elab.CheckTactic
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Command.lean
ADDED
|
@@ -0,0 +1,891 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Gabriel Ebner
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Diagnostics
|
| 8 |
+
import Lean.Elab.Binders
|
| 9 |
+
import Lean.Elab.SyntheticMVars
|
| 10 |
+
import Lean.Elab.SetOption
|
| 11 |
+
import Lean.Language.Basic
|
| 12 |
+
import Lean.Meta.ForEachExpr
|
| 13 |
+
|
| 14 |
+
namespace Lean.Elab.Command
|
| 15 |
+
|
| 16 |
+
/--
|
| 17 |
+
A `Scope` records the part of the `CommandElabM` state that respects scoping,
|
| 18 |
+
such as the data for `universe`, `open`, and `variable` declarations, the current namespace,
|
| 19 |
+
and currently enabled options.
|
| 20 |
+
The `CommandElabM` state contains a stack of scopes, and only the top `Scope`
|
| 21 |
+
on the stack is read from or modified. There is always at least one `Scope` on the stack,
|
| 22 |
+
even outside any `section` or `namespace`, and each new pushed `Scope`
|
| 23 |
+
starts as a modified copy of the previous top scope.
|
| 24 |
+
-/
|
| 25 |
+
structure Scope where
|
| 26 |
+
/--
|
| 27 |
+
The component of the `namespace` or `section` that this scope is associated to.
|
| 28 |
+
For example, `section a.b.c` and `namespace a.b.c` each create three scopes with headers
|
| 29 |
+
named `a`, `b`, and `c`.
|
| 30 |
+
This is used for checking the `end` command. The "base scope" has `""` as its header.
|
| 31 |
+
-/
|
| 32 |
+
header : String
|
| 33 |
+
/--
|
| 34 |
+
The current state of all set options at this point in the scope. Note that this is the
|
| 35 |
+
full current set of options and does *not* simply contain the options set
|
| 36 |
+
while this scope has been active.
|
| 37 |
+
-/
|
| 38 |
+
opts : Options := {}
|
| 39 |
+
/-- The current namespace. The top-level namespace is represented by `Name.anonymous`. -/
|
| 40 |
+
currNamespace : Name := Name.anonymous
|
| 41 |
+
/-- All currently `open`ed namespaces and names. -/
|
| 42 |
+
openDecls : List OpenDecl := []
|
| 43 |
+
/-- The current list of names for universe level variables to use for new declarations. This is managed by the `universe` command. -/
|
| 44 |
+
levelNames : List Name := []
|
| 45 |
+
/--
|
| 46 |
+
The current list of binders to use for new declarations.
|
| 47 |
+
This is managed by the `variable` command.
|
| 48 |
+
Each binder is represented in `Syntax` form, and it is re-elaborated
|
| 49 |
+
within each command that uses this information.
|
| 50 |
+
|
| 51 |
+
This is also used by commands, such as `#check`, to create an initial local context,
|
| 52 |
+
even if they do not work with binders per se.
|
| 53 |
+
-/
|
| 54 |
+
varDecls : Array (TSyntax ``Parser.Term.bracketedBinder) := #[]
|
| 55 |
+
/--
|
| 56 |
+
Globally unique internal identifiers for the `varDecls`.
|
| 57 |
+
There is one identifier per variable introduced by the binders
|
| 58 |
+
(recall that a binder such as `(a b c : Ty)` can produce more than one variable),
|
| 59 |
+
and each identifier is the user-provided variable name with a macro scope.
|
| 60 |
+
This is used by `TermElabM` in `Lean.Elab.Term.Context` to help with processing macros
|
| 61 |
+
that capture these variables.
|
| 62 |
+
-/
|
| 63 |
+
varUIds : Array Name := #[]
|
| 64 |
+
/-- `include`d section variable names (from `varUIds`) -/
|
| 65 |
+
includedVars : List Name := []
|
| 66 |
+
/-- `omit`ted section variable names (from `varUIds`) -/
|
| 67 |
+
omittedVars : List Name := []
|
| 68 |
+
/--
|
| 69 |
+
If true (default: false), all declarations that fail to compile
|
| 70 |
+
automatically receive the `noncomputable` modifier.
|
| 71 |
+
A scope with this flag set is created by `noncomputable section`.
|
| 72 |
+
|
| 73 |
+
Recall that a new scope inherits all values from its parent scope,
|
| 74 |
+
so all sections and namespaces nested within a `noncomputable` section also have this flag set.
|
| 75 |
+
-/
|
| 76 |
+
isNoncomputable : Bool := false
|
| 77 |
+
isPublic : Bool := false
|
| 78 |
+
/--
|
| 79 |
+
Attributes that should be applied to all matching declaration in the section. Inherited from
|
| 80 |
+
parent scopes.
|
| 81 |
+
-/
|
| 82 |
+
attrs : List (TSyntax ``Parser.Term.attrInstance) := []
|
| 83 |
+
deriving Inhabited
|
| 84 |
+
|
| 85 |
+
structure State where
|
| 86 |
+
env : Environment
|
| 87 |
+
messages : MessageLog := {}
|
| 88 |
+
scopes : List Scope := [{ header := "" }]
|
| 89 |
+
nextMacroScope : Nat := firstFrontendMacroScope + 1
|
| 90 |
+
maxRecDepth : Nat
|
| 91 |
+
ngen : NameGenerator := {}
|
| 92 |
+
auxDeclNGen : DeclNameGenerator := {}
|
| 93 |
+
infoState : InfoState := {}
|
| 94 |
+
traceState : TraceState := {}
|
| 95 |
+
snapshotTasks : Array (Language.SnapshotTask Language.SnapshotTree) := #[]
|
| 96 |
+
deriving Nonempty
|
| 97 |
+
|
| 98 |
+
structure Context where
|
| 99 |
+
fileName : String
|
| 100 |
+
fileMap : FileMap
|
| 101 |
+
currRecDepth : Nat := 0
|
| 102 |
+
cmdPos : String.Pos := 0
|
| 103 |
+
macroStack : MacroStack := []
|
| 104 |
+
currMacroScope : MacroScope := firstFrontendMacroScope
|
| 105 |
+
ref : Syntax := Syntax.missing
|
| 106 |
+
/--
|
| 107 |
+
Snapshot for incremental reuse and reporting of command elaboration. Currently only used for
|
| 108 |
+
(mutual) defs and contained tactics, in which case the `DynamicSnapshot` is a
|
| 109 |
+
`HeadersParsedSnapshot`.
|
| 110 |
+
|
| 111 |
+
Definitely resolved in `Lean.Elab.Command.elabCommandTopLevel`.
|
| 112 |
+
|
| 113 |
+
Invariant: if the bundle's `old?` is set, the context and state at the beginning of current and
|
| 114 |
+
old elaboration are identical.
|
| 115 |
+
-/
|
| 116 |
+
snap? : Option (Language.SnapshotBundle Language.DynamicSnapshot)
|
| 117 |
+
/-- Cancellation token forwarded to `Core.cancelTk?`. -/
|
| 118 |
+
cancelTk? : Option IO.CancelToken
|
| 119 |
+
/--
|
| 120 |
+
If set (when `showPartialSyntaxErrors` is not set and parsing failed), suppresses most elaboration
|
| 121 |
+
errors; see also `logMessage` below.
|
| 122 |
+
-/
|
| 123 |
+
suppressElabErrors : Bool := false
|
| 124 |
+
|
| 125 |
+
abbrev CommandElabM := ReaderT Context $ StateRefT State $ EIO Exception
|
| 126 |
+
abbrev CommandElab := Syntax β CommandElabM Unit
|
| 127 |
+
structure Linter where
|
| 128 |
+
run : Syntax β CommandElabM Unit
|
| 129 |
+
name : Name := by exact decl_name%
|
| 130 |
+
|
| 131 |
+
/-
|
| 132 |
+
Make the compiler generate specialized `pure`/`bind` so we do not have to optimize through the
|
| 133 |
+
whole monad stack at every use site. May eventually be covered by `deriving`.
|
| 134 |
+
|
| 135 |
+
Remark: see comment at TermElabM
|
| 136 |
+
-/
|
| 137 |
+
@[always_inline]
|
| 138 |
+
instance : Monad CommandElabM := let i := inferInstanceAs (Monad CommandElabM); { pure := i.pure, bind := i.bind }
|
| 139 |
+
|
| 140 |
+
/--
|
| 141 |
+
Like `Core.tryCatchRuntimeEx`; runtime errors are generally used to abort term elaboration, so we do
|
| 142 |
+
want to catch and process them at the command level.
|
| 143 |
+
-/
|
| 144 |
+
@[inline] protected def tryCatch (x : CommandElabM Ξ±) (h : Exception β CommandElabM Ξ±) :
|
| 145 |
+
CommandElabM Ξ± := do
|
| 146 |
+
try
|
| 147 |
+
x
|
| 148 |
+
catch ex =>
|
| 149 |
+
if ex.isInterrupt then
|
| 150 |
+
throw ex
|
| 151 |
+
else
|
| 152 |
+
h ex
|
| 153 |
+
|
| 154 |
+
instance : MonadExceptOf Exception CommandElabM where
|
| 155 |
+
throw := throw
|
| 156 |
+
tryCatch := Command.tryCatch
|
| 157 |
+
|
| 158 |
+
def mkState (env : Environment) (messages : MessageLog := {}) (opts : Options := {}) : State := {
|
| 159 |
+
env := env
|
| 160 |
+
messages := messages
|
| 161 |
+
scopes := [{ header := "", opts }]
|
| 162 |
+
maxRecDepth := maxRecDepth.get opts
|
| 163 |
+
-- Outside of declarations, fall back to a module-specific prefix
|
| 164 |
+
auxDeclNGen := { namePrefix := mkPrivateName env .anonymous }
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
/- Linters should be loadable as plugins, so store in a global IO ref instead of an attribute managed by the
|
| 168 |
+
environment (which only contains `import`ed objects). -/
|
| 169 |
+
builtin_initialize lintersRef : IO.Ref (Array Linter) β IO.mkRef #[]
|
| 170 |
+
builtin_initialize registerTraceClass `Elab.lint
|
| 171 |
+
|
| 172 |
+
def addLinter (l : Linter) : IO Unit := do
|
| 173 |
+
let ls β lintersRef.get
|
| 174 |
+
lintersRef.set (ls.push l)
|
| 175 |
+
|
| 176 |
+
instance : MonadInfoTree CommandElabM where
|
| 177 |
+
getInfoState := return (β get).infoState
|
| 178 |
+
modifyInfoState f := modify fun s => { s with infoState := f s.infoState }
|
| 179 |
+
|
| 180 |
+
instance : MonadEnv CommandElabM where
|
| 181 |
+
getEnv := do pure (β get).env
|
| 182 |
+
modifyEnv f := modify fun s => { s with env := f s.env }
|
| 183 |
+
|
| 184 |
+
@[always_inline]
|
| 185 |
+
instance : MonadOptions CommandElabM where
|
| 186 |
+
getOptions := do pure (β get).scopes.head!.opts
|
| 187 |
+
|
| 188 |
+
protected def getRef : CommandElabM Syntax :=
|
| 189 |
+
return (β read).ref
|
| 190 |
+
|
| 191 |
+
instance : AddMessageContext CommandElabM where
|
| 192 |
+
addMessageContext := addMessageContextPartial
|
| 193 |
+
|
| 194 |
+
instance : MonadRef CommandElabM where
|
| 195 |
+
getRef := Command.getRef
|
| 196 |
+
withRef ref x := withReader (fun ctx => { ctx with ref := ref }) x
|
| 197 |
+
|
| 198 |
+
instance : MonadTrace CommandElabM where
|
| 199 |
+
getTraceState := return (β get).traceState
|
| 200 |
+
modifyTraceState f := modify fun s => { s with traceState := f s.traceState }
|
| 201 |
+
|
| 202 |
+
instance : AddErrorMessageContext CommandElabM where
|
| 203 |
+
add ref msg := do
|
| 204 |
+
let ctx β read
|
| 205 |
+
let ref := getBetterRef ref ctx.macroStack
|
| 206 |
+
let msg β addMessageContext msg
|
| 207 |
+
let msg β addMacroStack msg ctx.macroStack
|
| 208 |
+
return (ref, msg)
|
| 209 |
+
|
| 210 |
+
instance : MonadDeclNameGenerator CommandElabM where
|
| 211 |
+
getDeclNGen := return (β get).auxDeclNGen
|
| 212 |
+
setDeclNGen ngen := modify fun s => { s with auxDeclNGen := ngen }
|
| 213 |
+
|
| 214 |
+
private def runCore (x : CoreM Ξ±) : CommandElabM Ξ± := do
|
| 215 |
+
let s β get
|
| 216 |
+
let ctx β read
|
| 217 |
+
let heartbeats β IO.getNumHeartbeats
|
| 218 |
+
let env := Kernel.resetDiag s.env
|
| 219 |
+
let scope := s.scopes.head!
|
| 220 |
+
let coreCtx : Core.Context := {
|
| 221 |
+
fileName := ctx.fileName
|
| 222 |
+
fileMap := ctx.fileMap
|
| 223 |
+
currRecDepth := ctx.currRecDepth
|
| 224 |
+
maxRecDepth := s.maxRecDepth
|
| 225 |
+
ref := ctx.ref
|
| 226 |
+
currNamespace := scope.currNamespace
|
| 227 |
+
openDecls := scope.openDecls
|
| 228 |
+
initHeartbeats := heartbeats
|
| 229 |
+
currMacroScope := ctx.currMacroScope
|
| 230 |
+
options := scope.opts
|
| 231 |
+
cancelTk? := ctx.cancelTk?
|
| 232 |
+
suppressElabErrors := ctx.suppressElabErrors }
|
| 233 |
+
let x : EIO _ _ := x.run coreCtx {
|
| 234 |
+
env
|
| 235 |
+
ngen := s.ngen
|
| 236 |
+
auxDeclNGen := s.auxDeclNGen
|
| 237 |
+
nextMacroScope := s.nextMacroScope
|
| 238 |
+
infoState.enabled := s.infoState.enabled
|
| 239 |
+
traceState := s.traceState
|
| 240 |
+
snapshotTasks := s.snapshotTasks
|
| 241 |
+
}
|
| 242 |
+
let (ea, coreS) β liftM x
|
| 243 |
+
modify fun s => { s with
|
| 244 |
+
env := coreS.env
|
| 245 |
+
nextMacroScope := coreS.nextMacroScope
|
| 246 |
+
ngen := coreS.ngen
|
| 247 |
+
auxDeclNGen := coreS.auxDeclNGen
|
| 248 |
+
infoState.trees := s.infoState.trees.append coreS.infoState.trees
|
| 249 |
+
-- we assume substitution of `assignment` has already happened, but for lazy assignments we only
|
| 250 |
+
-- do it at the very end
|
| 251 |
+
infoState.lazyAssignment := coreS.infoState.lazyAssignment
|
| 252 |
+
traceState.traces := coreS.traceState.traces.map fun t => { t with ref := replaceRef t.ref ctx.ref }
|
| 253 |
+
snapshotTasks := coreS.snapshotTasks
|
| 254 |
+
messages := s.messages ++ coreS.messages
|
| 255 |
+
}
|
| 256 |
+
return ea
|
| 257 |
+
|
| 258 |
+
def liftCoreM (x : CoreM Ξ±) : CommandElabM Ξ± := do
|
| 259 |
+
MonadExcept.ofExcept (β runCore (observing x))
|
| 260 |
+
|
| 261 |
+
@[inline] def liftIO {Ξ±} (x : IO Ξ±) : CommandElabM Ξ± := do
|
| 262 |
+
let ctx β read
|
| 263 |
+
IO.toEIO (fun (ex : IO.Error) => Exception.error ctx.ref ex.toString) x
|
| 264 |
+
|
| 265 |
+
instance : MonadLiftT IO CommandElabM where
|
| 266 |
+
monadLift := liftIO
|
| 267 |
+
|
| 268 |
+
/-- Return the current scope. -/
|
| 269 |
+
def getScope : CommandElabM Scope := do pure (β get).scopes.head!
|
| 270 |
+
|
| 271 |
+
instance : MonadResolveName CommandElabM where
|
| 272 |
+
getCurrNamespace := return (β getScope).currNamespace
|
| 273 |
+
getOpenDecls := return (β getScope).openDecls
|
| 274 |
+
|
| 275 |
+
instance : MonadLog CommandElabM where
|
| 276 |
+
getRef := getRef
|
| 277 |
+
getFileMap := return (β read).fileMap
|
| 278 |
+
getFileName := return (β read).fileName
|
| 279 |
+
hasErrors := return (β get).messages.hasErrors
|
| 280 |
+
logMessage msg := do
|
| 281 |
+
if (β read).suppressElabErrors then
|
| 282 |
+
-- discard elaboration errors on parse error
|
| 283 |
+
unless msg.data.hasTag (Β· matches `trace) do
|
| 284 |
+
return
|
| 285 |
+
let currNamespace β getCurrNamespace
|
| 286 |
+
let openDecls β getOpenDecls
|
| 287 |
+
let msg := { msg with data := MessageData.withNamingContext { currNamespace := currNamespace, openDecls := openDecls } msg.data }
|
| 288 |
+
modify fun s => { s with messages := s.messages.add msg }
|
| 289 |
+
|
| 290 |
+
def runLinters (stx : Syntax) : CommandElabM Unit := do
|
| 291 |
+
profileitM Exception "linting" (β getOptions) do
|
| 292 |
+
withTraceNode `Elab.lint (fun _ => return m!"running linters") do
|
| 293 |
+
let linters β lintersRef.get
|
| 294 |
+
unless linters.isEmpty do
|
| 295 |
+
for linter in linters do
|
| 296 |
+
withTraceNode `Elab.lint (fun _ => return m!"running linter: {.ofConstName linter.name}")
|
| 297 |
+
(tag := linter.name.toString) do
|
| 298 |
+
let savedState β get
|
| 299 |
+
try
|
| 300 |
+
linter.run stx
|
| 301 |
+
catch ex =>
|
| 302 |
+
match ex with
|
| 303 |
+
| Exception.error ref msg =>
|
| 304 |
+
logException (.error ref m!"linter {.ofConstName linter.name} failed: {msg}")
|
| 305 |
+
| Exception.internal _ _ =>
|
| 306 |
+
logException ex
|
| 307 |
+
finally
|
| 308 |
+
-- TODO: it would be good to preserve even more state (#4363) but preserving info
|
| 309 |
+
-- trees currently breaks from linters adding context-less info nodes
|
| 310 |
+
modify fun s => { savedState with messages := s.messages, traceState := s.traceState }
|
| 311 |
+
|
| 312 |
+
/--
|
| 313 |
+
Catches and logs exceptions occurring in `x`. Unlike `try catch` in `CommandElabM`, this function
|
| 314 |
+
catches interrupt exceptions as well and thus is intended for use at the top level of elaboration.
|
| 315 |
+
Interrupt and abort exceptions are caught but not logged.
|
| 316 |
+
-/
|
| 317 |
+
@[inline] def withLoggingExceptions (x : CommandElabM Unit) : CommandElabM Unit := fun ctx ref =>
|
| 318 |
+
EIO.catchExceptions (withLogging x ctx ref) (fun _ => pure ())
|
| 319 |
+
|
| 320 |
+
@[inherit_doc Core.wrapAsync]
|
| 321 |
+
def wrapAsync {Ξ± Ξ² : Type} (act : Ξ± β CommandElabM Ξ²) (cancelTk? : Option IO.CancelToken) :
|
| 322 |
+
CommandElabM (Ξ± β EIO Exception Ξ²) := do
|
| 323 |
+
let ctx β read
|
| 324 |
+
let ctx := { ctx with cancelTk? }
|
| 325 |
+
let (childNGen, parentNGen) := (β getDeclNGen).mkChild
|
| 326 |
+
setDeclNGen parentNGen
|
| 327 |
+
let st β get
|
| 328 |
+
let st := { st with auxDeclNGen := childNGen }
|
| 329 |
+
return (act Β· |>.run ctx |>.run' st)
|
| 330 |
+
|
| 331 |
+
open Language in
|
| 332 |
+
@[inherit_doc Core.wrapAsyncAsSnapshot]
|
| 333 |
+
-- `CoreM` and `CommandElabM` are too different to meaningfully share this code
|
| 334 |
+
def wrapAsyncAsSnapshot {Ξ± : Type} (act : Ξ± β CommandElabM Unit) (cancelTk? : Option IO.CancelToken)
|
| 335 |
+
(desc : String := by exact decl_name%.toString) :
|
| 336 |
+
CommandElabM (Ξ± β BaseIO SnapshotTree) := do
|
| 337 |
+
let f β wrapAsync (cancelTk? := cancelTk?) fun a => do
|
| 338 |
+
IO.FS.withIsolatedStreams (isolateStderr := Core.stderrAsMessages.get (β getOptions)) do
|
| 339 |
+
let tid β IO.getTID
|
| 340 |
+
-- reset trace state and message log so as not to report them twice
|
| 341 |
+
modify fun st => { st with
|
| 342 |
+
messages := st.messages.markAllReported
|
| 343 |
+
traceState := { tid }
|
| 344 |
+
snapshotTasks := #[]
|
| 345 |
+
}
|
| 346 |
+
try
|
| 347 |
+
withTraceNode `Elab.async (fun _ => return desc) do
|
| 348 |
+
act a
|
| 349 |
+
catch e =>
|
| 350 |
+
logError e.toMessageData
|
| 351 |
+
finally
|
| 352 |
+
addTraceAsMessages
|
| 353 |
+
get
|
| 354 |
+
let ctx β read
|
| 355 |
+
return fun a => do
|
| 356 |
+
match (β (f a).toBaseIO) with
|
| 357 |
+
| .ok (output, st) =>
|
| 358 |
+
let mut msgs := st.messages
|
| 359 |
+
if !output.isEmpty then
|
| 360 |
+
msgs := msgs.add {
|
| 361 |
+
fileName := ctx.fileName
|
| 362 |
+
severity := MessageSeverity.information
|
| 363 |
+
pos := ctx.fileMap.toPosition <| ctx.ref.getPos?.getD 0
|
| 364 |
+
data := output
|
| 365 |
+
}
|
| 366 |
+
return .mk {
|
| 367 |
+
desc
|
| 368 |
+
diagnostics := (β Language.Snapshot.Diagnostics.ofMessageLog msgs)
|
| 369 |
+
traces := st.traceState
|
| 370 |
+
} st.snapshotTasks
|
| 371 |
+
-- interrupt or abort exception as `try catch` above should have caught any others
|
| 372 |
+
| .error _ => default
|
| 373 |
+
|
| 374 |
+
@[inherit_doc Core.logSnapshotTask]
|
| 375 |
+
def logSnapshotTask (task : Language.SnapshotTask Language.SnapshotTree) : CommandElabM Unit :=
|
| 376 |
+
modify fun s => { s with snapshotTasks := s.snapshotTasks.push task }
|
| 377 |
+
|
| 378 |
+
open Language in
|
| 379 |
+
def runLintersAsync (stx : Syntax) : CommandElabM Unit := do
|
| 380 |
+
if !Elab.async.get (β getOptions) then
|
| 381 |
+
withoutModifyingEnv do
|
| 382 |
+
runLinters stx
|
| 383 |
+
return
|
| 384 |
+
|
| 385 |
+
-- linters should have access to the complete info tree and message log
|
| 386 |
+
let mut snaps := (β get).snapshotTasks
|
| 387 |
+
if let some elabSnap := (β read).snap? then
|
| 388 |
+
snaps := snaps.push { stx? := none, cancelTk? := none, task := elabSnap.new.result!.map (sync := true) toSnapshotTree }
|
| 389 |
+
let tree := SnapshotTree.mk { diagnostics := .empty } snaps
|
| 390 |
+
let treeTask β tree.waitAll
|
| 391 |
+
|
| 392 |
+
-- We only start one task for all linters for now as most linters are fast and we simply want
|
| 393 |
+
-- to unblock elaboration of the next command
|
| 394 |
+
let cancelTk β IO.CancelToken.new
|
| 395 |
+
let lintAct β wrapAsyncAsSnapshot (cancelTk? := cancelTk) fun infoSt => do
|
| 396 |
+
let messages := tree.getAll.map (Β·.diagnostics.msgLog) |>.foldl (Β· ++ Β·) .empty
|
| 397 |
+
-- do not double-report
|
| 398 |
+
let messages := messages.markAllReported
|
| 399 |
+
modify fun st => { st with messages := st.messages ++ messages }
|
| 400 |
+
modifyInfoState fun _ => infoSt
|
| 401 |
+
runLinters stx
|
| 402 |
+
|
| 403 |
+
let task β BaseIO.bindTask (sync := true) (t := (β getInfoState).substituteLazy) fun infoSt =>
|
| 404 |
+
BaseIO.mapTask (t := treeTask) fun _ =>
|
| 405 |
+
lintAct infoSt
|
| 406 |
+
logSnapshotTask { stx? := none, task, cancelTk? := cancelTk }
|
| 407 |
+
|
| 408 |
+
protected def getCurrMacroScope : CommandElabM Nat := do pure (β read).currMacroScope
|
| 409 |
+
protected def getMainModule : CommandElabM Name := do pure (β getEnv).mainModule
|
| 410 |
+
|
| 411 |
+
protected def withFreshMacroScope {Ξ±} (x : CommandElabM Ξ±) : CommandElabM Ξ± := do
|
| 412 |
+
let fresh β modifyGet (fun st => (st.nextMacroScope, { st with nextMacroScope := st.nextMacroScope + 1 }))
|
| 413 |
+
withReader (fun ctx => { ctx with currMacroScope := fresh }) x
|
| 414 |
+
|
| 415 |
+
instance : MonadQuotation CommandElabM where
|
| 416 |
+
getCurrMacroScope := Command.getCurrMacroScope
|
| 417 |
+
getMainModule := Command.getMainModule
|
| 418 |
+
withFreshMacroScope := Command.withFreshMacroScope
|
| 419 |
+
|
| 420 |
+
/--
|
| 421 |
+
Registers a command elaborator for the given syntax node kind.
|
| 422 |
+
|
| 423 |
+
A command elaborator should have type `Lean.Elab.Command.CommandElab` (which is
|
| 424 |
+
`Lean.Syntax β Lean.Elab.Term.CommandElabM Unit`), i.e. should take syntax of the given syntax
|
| 425 |
+
node kind as a parameter and perform an action.
|
| 426 |
+
|
| 427 |
+
The `elab_rules` and `elab` commands should usually be preferred over using this attribute
|
| 428 |
+
directly.
|
| 429 |
+
-/
|
| 430 |
+
@[builtin_doc]
|
| 431 |
+
unsafe builtin_initialize commandElabAttribute : KeyedDeclsAttribute CommandElab β
|
| 432 |
+
mkElabAttribute CommandElab `builtin_command_elab `command_elab `Lean.Parser.Command `Lean.Elab.Command.CommandElab "command"
|
| 433 |
+
|
| 434 |
+
private def mkInfoTree (elaborator : Name) (stx : Syntax) (trees : PersistentArray InfoTree) : CommandElabM InfoTree := do
|
| 435 |
+
let ctx β read
|
| 436 |
+
let s β get
|
| 437 |
+
let scope := s.scopes.head!
|
| 438 |
+
let tree := InfoTree.node (Info.ofCommandInfo { elaborator, stx }) trees
|
| 439 |
+
let ctx := PartialContextInfo.commandCtx {
|
| 440 |
+
env := s.env, fileMap := ctx.fileMap, mctx := {}, currNamespace := scope.currNamespace,
|
| 441 |
+
openDecls := scope.openDecls, options := scope.opts, ngen := s.ngen
|
| 442 |
+
}
|
| 443 |
+
return InfoTree.context ctx tree
|
| 444 |
+
|
| 445 |
+
/--
|
| 446 |
+
Disables incremental command reuse *and* reporting for `act` if `cond` is true by setting
|
| 447 |
+
`Context.snap?` to `none`.
|
| 448 |
+
-/
|
| 449 |
+
def withoutCommandIncrementality (cond : Bool) (act : CommandElabM Ξ±) : CommandElabM Ξ± := do
|
| 450 |
+
let opts β getOptions
|
| 451 |
+
withReader (fun ctx => { ctx with snap? := ctx.snap?.filter fun snap => Id.run do
|
| 452 |
+
if let some old := snap.old? then
|
| 453 |
+
if cond && opts.getBool `trace.Elab.reuse then
|
| 454 |
+
dbg_trace "reuse stopped: guard failed at {old.stx}"
|
| 455 |
+
return !cond
|
| 456 |
+
}) act
|
| 457 |
+
|
| 458 |
+
private def elabCommandUsing (s : State) (stx : Syntax) : List (KeyedDeclsAttribute.AttributeEntry CommandElab) β CommandElabM Unit
|
| 459 |
+
| [] => withInfoTreeContext (mkInfoTree := mkInfoTree `no_elab stx) <| throwError "unexpected syntax{indentD stx}"
|
| 460 |
+
| (elabFn::elabFns) =>
|
| 461 |
+
catchInternalId unsupportedSyntaxExceptionId
|
| 462 |
+
(do
|
| 463 |
+
-- prevent unsupported commands from accidentally accessing `Context.snap?` (e.g. by nested
|
| 464 |
+
-- supported commands)
|
| 465 |
+
withoutCommandIncrementality (!(β isIncrementalElab elabFn.declName)) do
|
| 466 |
+
withInfoTreeContext (mkInfoTree := mkInfoTree elabFn.declName stx) do
|
| 467 |
+
elabFn.value stx)
|
| 468 |
+
(fun _ => do set s; elabCommandUsing s stx elabFns)
|
| 469 |
+
|
| 470 |
+
/-- Elaborate `x` with `stx` on the macro stack -/
|
| 471 |
+
def withMacroExpansion (beforeStx afterStx : Syntax) (x : CommandElabM Ξ±) : CommandElabM Ξ± :=
|
| 472 |
+
withInfoContext (mkInfo := pure <| .ofMacroExpansionInfo { stx := beforeStx, output := afterStx, lctx := .empty }) do
|
| 473 |
+
withReader (fun ctx => { ctx with macroStack := { before := beforeStx, after := afterStx } :: ctx.macroStack }) x
|
| 474 |
+
|
| 475 |
+
instance : MonadMacroAdapter CommandElabM where
|
| 476 |
+
getCurrMacroScope := getCurrMacroScope
|
| 477 |
+
getNextMacroScope := return (β get).nextMacroScope
|
| 478 |
+
setNextMacroScope next := modify fun s => { s with nextMacroScope := next }
|
| 479 |
+
|
| 480 |
+
instance : MonadRecDepth CommandElabM where
|
| 481 |
+
withRecDepth d x := withReader (fun ctx => { ctx with currRecDepth := d }) x
|
| 482 |
+
getRecDepth := return (β read).currRecDepth
|
| 483 |
+
getMaxRecDepth := return (β get).maxRecDepth
|
| 484 |
+
|
| 485 |
+
builtin_initialize registerTraceClass `Elab.command
|
| 486 |
+
|
| 487 |
+
open Language in
|
| 488 |
+
/-- Snapshot after macro expansion of a command. -/
|
| 489 |
+
structure MacroExpandedSnapshot extends Snapshot where
|
| 490 |
+
/-- The declaration name of the macro. -/
|
| 491 |
+
macroDecl : Name
|
| 492 |
+
/-- The expanded syntax tree. -/
|
| 493 |
+
newStx : Syntax
|
| 494 |
+
/-- `State.nextMacroScope` after expansion. -/
|
| 495 |
+
newNextMacroScope : Nat
|
| 496 |
+
/-- Whether any traces were present after expansion. -/
|
| 497 |
+
hasTraces : Bool
|
| 498 |
+
/--
|
| 499 |
+
Follow-up elaboration snapshots, one per command if `newStx` is a sequence of commands.
|
| 500 |
+
-/
|
| 501 |
+
next : Array (SnapshotTask DynamicSnapshot)
|
| 502 |
+
deriving TypeName
|
| 503 |
+
open Language in
|
| 504 |
+
instance : ToSnapshotTree MacroExpandedSnapshot where
|
| 505 |
+
toSnapshotTree s := β¨s.toSnapshot, s.next.map (Β·.map (sync := true) toSnapshotTree)β©
|
| 506 |
+
|
| 507 |
+
partial def elabCommand (stx : Syntax) : CommandElabM Unit :=
|
| 508 |
+
try
|
| 509 |
+
go
|
| 510 |
+
finally
|
| 511 |
+
addTraceAsMessages
|
| 512 |
+
where go := do
|
| 513 |
+
withLogging <| withRef stx <| withIncRecDepth <| withFreshMacroScope do
|
| 514 |
+
match stx with
|
| 515 |
+
| Syntax.node _ k args =>
|
| 516 |
+
if k == nullKind then
|
| 517 |
+
-- list of commands => elaborate in order
|
| 518 |
+
-- The parser will only ever return a single command at a time, but syntax quotations can return multiple ones
|
| 519 |
+
-- Incrementality is currently limited to the common case where the sequence is the direct
|
| 520 |
+
-- output of a macro, see below.
|
| 521 |
+
withoutCommandIncrementality true do
|
| 522 |
+
args.forM elabCommand
|
| 523 |
+
else withTraceNode `Elab.command (fun _ => return stx) (tag :=
|
| 524 |
+
-- special case: show actual declaration kind for `declaration` commands
|
| 525 |
+
(if stx.isOfKind ``Parser.Command.declaration then stx[1] else stx).getKind.toString) do
|
| 526 |
+
let s β get
|
| 527 |
+
match (β liftMacroM <| expandMacroImpl? s.env stx) with
|
| 528 |
+
| some (decl, stxNew?) =>
|
| 529 |
+
withInfoTreeContext (mkInfoTree := mkInfoTree decl stx) do
|
| 530 |
+
let stxNew β liftMacroM <| liftExcept stxNew?
|
| 531 |
+
withMacroExpansion stx stxNew do
|
| 532 |
+
-- Support incrementality; see also Note [Incremental Macros]
|
| 533 |
+
if let some snap := (βread).snap? then
|
| 534 |
+
-- Unpack nested commands; see `MacroExpandedSnapshot.next`
|
| 535 |
+
let cmds := if stxNew.isOfKind nullKind then stxNew.getArgs else #[stxNew]
|
| 536 |
+
let nextMacroScope := (β get).nextMacroScope
|
| 537 |
+
let hasTraces := (β getTraceState).traces.size > 0
|
| 538 |
+
let oldSnap? := do
|
| 539 |
+
let oldSnap β snap.old?
|
| 540 |
+
let oldSnap β oldSnap.val.get.toTyped? MacroExpandedSnapshot
|
| 541 |
+
guard <| oldSnap.macroDecl == decl && oldSnap.newNextMacroScope == nextMacroScope
|
| 542 |
+
-- check absence of traces; see Note [Incremental Macros]
|
| 543 |
+
guard <| !oldSnap.hasTraces && !hasTraces
|
| 544 |
+
return oldSnap
|
| 545 |
+
if snap.old?.isSome && oldSnap?.isNone then
|
| 546 |
+
snap.old?.forM (Β·.val.cancelRec)
|
| 547 |
+
let oldCmds? := oldSnap?.map fun old =>
|
| 548 |
+
if old.newStx.isOfKind nullKind then old.newStx.getArgs else #[old.newStx]
|
| 549 |
+
let cmdPromises β cmds.mapM fun _ => IO.Promise.new
|
| 550 |
+
let cancelTk? := (β read).cancelTk?
|
| 551 |
+
snap.new.resolve <| .ofTyped {
|
| 552 |
+
diagnostics := .empty
|
| 553 |
+
macroDecl := decl
|
| 554 |
+
newStx := stxNew
|
| 555 |
+
newNextMacroScope := nextMacroScope
|
| 556 |
+
hasTraces
|
| 557 |
+
next := Array.zipWith (fun cmdPromise cmd =>
|
| 558 |
+
{ stx? := some cmd, task := cmdPromise.resultD default, cancelTk? }) cmdPromises cmds
|
| 559 |
+
: MacroExpandedSnapshot
|
| 560 |
+
}
|
| 561 |
+
-- After the first command whose syntax tree changed, we must disable
|
| 562 |
+
-- incremental reuse
|
| 563 |
+
let mut reusedCmds := true
|
| 564 |
+
let opts β getOptions
|
| 565 |
+
-- For each command, associate it with new promise and old snapshot, if any, and
|
| 566 |
+
-- elaborate recursively
|
| 567 |
+
for cmd in cmds, cmdPromise in cmdPromises, i in [0:cmds.size] do
|
| 568 |
+
let oldCmd? := oldCmds?.bind (Β·[i]?)
|
| 569 |
+
withReader ({ Β· with snap? := some {
|
| 570 |
+
new := cmdPromise
|
| 571 |
+
old? := do
|
| 572 |
+
guard reusedCmds
|
| 573 |
+
let old β oldSnap?
|
| 574 |
+
return { stx := (β oldCmd?), val := (β old.next[i]?) }
|
| 575 |
+
} }) do
|
| 576 |
+
if oldSnap?.isSome && (β read).snap?.isNone then
|
| 577 |
+
oldSnap?.bind (Β·.next[i]?) |>.forM (Β·.cancelRec)
|
| 578 |
+
elabCommand cmd
|
| 579 |
+
-- Resolve promise for commands not supporting incrementality; waiting for
|
| 580 |
+
-- `withAlwaysResolvedPromises` to do this could block reporting by later
|
| 581 |
+
-- commands
|
| 582 |
+
cmdPromise.resolve default
|
| 583 |
+
reusedCmds := reusedCmds && oldCmd?.any (Β·.eqWithInfoAndTraceReuse opts cmd)
|
| 584 |
+
else
|
| 585 |
+
elabCommand stxNew
|
| 586 |
+
| _ =>
|
| 587 |
+
match commandElabAttribute.getEntries s.env k with
|
| 588 |
+
| [] =>
|
| 589 |
+
withInfoTreeContext (mkInfoTree := mkInfoTree `no_elab stx) <|
|
| 590 |
+
throwError "elaboration function for '{k}' has not been implemented"
|
| 591 |
+
| elabFns => elabCommandUsing s stx elabFns
|
| 592 |
+
| _ =>
|
| 593 |
+
withInfoTreeContext (mkInfoTree := mkInfoTree `no_elab stx) <|
|
| 594 |
+
throwError "unexpected command"
|
| 595 |
+
|
| 596 |
+
builtin_initialize registerTraceClass `Elab.input
|
| 597 |
+
|
| 598 |
+
/-- Option for showing elaboration errors from partial syntax errors. -/
|
| 599 |
+
register_builtin_option showPartialSyntaxErrors : Bool := {
|
| 600 |
+
defValue := false
|
| 601 |
+
descr := "show elaboration errors from partial syntax trees (i.e. after parser recovery)"
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
builtin_initialize
|
| 605 |
+
registerTraceClass `Elab.info
|
| 606 |
+
registerTraceClass `Elab.snapshotTree
|
| 607 |
+
|
| 608 |
+
/--
|
| 609 |
+
`elabCommand` wrapper that should be used for the initial invocation, not for recursive calls after
|
| 610 |
+
macro expansion etc.
|
| 611 |
+
-/
|
| 612 |
+
def elabCommandTopLevel (stx : Syntax) : CommandElabM Unit := withRef stx do profileitM Exception "elaboration" (β getOptions) do
|
| 613 |
+
withReader ({ Β· with suppressElabErrors :=
|
| 614 |
+
stx.hasMissing && !showPartialSyntaxErrors.get (β getOptions) }) do
|
| 615 |
+
let initMsgs β modifyGet fun st => (st.messages, { st with messages := {} })
|
| 616 |
+
let initInfoTrees β getResetInfoTrees
|
| 617 |
+
try
|
| 618 |
+
try
|
| 619 |
+
-- We should *not* factor out `elabCommand`'s `withLogging` to here since it would make its error
|
| 620 |
+
-- recovery more coarse. In particular, if `c` in `set_option ... in $c` fails, the remaining
|
| 621 |
+
-- `end` command of the `in` macro would be skipped and the option would be leaked to the outside!
|
| 622 |
+
elabCommand stx
|
| 623 |
+
finally
|
| 624 |
+
-- Make sure `snap?` is definitely resolved; we do not use it for reporting as `#guard_msgs` may
|
| 625 |
+
-- be the caller of this function and add new messages and info trees
|
| 626 |
+
if let some snap := (β read).snap? then
|
| 627 |
+
snap.new.resolve default
|
| 628 |
+
|
| 629 |
+
-- Run the linters, unless `#guard_msgs` is present, which is special and runs `elabCommandTopLevel` itself,
|
| 630 |
+
-- so it is a "super-top-level" command. This is the only command that does this, so we just special case it here
|
| 631 |
+
-- rather than engineer a general solution.
|
| 632 |
+
unless (stx.find? (Β·.isOfKind ``Lean.guardMsgsCmd)).isSome do
|
| 633 |
+
withLogging do
|
| 634 |
+
runLintersAsync stx
|
| 635 |
+
finally
|
| 636 |
+
let msgs := (β get).messages
|
| 637 |
+
modify fun st => { st with
|
| 638 |
+
messages := initMsgs ++ msgs
|
| 639 |
+
infoState := { st.infoState with trees := initInfoTrees ++ st.infoState.trees }
|
| 640 |
+
}
|
| 641 |
+
|
| 642 |
+
/-- Adapt a syntax transformation to a regular, command-producing elaborator. -/
|
| 643 |
+
def adaptExpander (exp : Syntax β CommandElabM Syntax) : CommandElab := fun stx => do
|
| 644 |
+
let stx' β exp stx
|
| 645 |
+
withMacroExpansion stx stx' <| elabCommand stx'
|
| 646 |
+
|
| 647 |
+
private def getVarDecls (s : State) : Array Syntax :=
|
| 648 |
+
s.scopes.head!.varDecls
|
| 649 |
+
|
| 650 |
+
instance {Ξ±} : Inhabited (CommandElabM Ξ±) where
|
| 651 |
+
default := throw default
|
| 652 |
+
|
| 653 |
+
/--
|
| 654 |
+
The environment linter framework needs to be able to run linters with the same context
|
| 655 |
+
as `liftTermElabM`, so we expose that context as a public function here.
|
| 656 |
+
-/
|
| 657 |
+
def mkMetaContext : Meta.Context := {
|
| 658 |
+
config := { foApprox := true, ctxApprox := true, quasiPatternApprox := true }
|
| 659 |
+
}
|
| 660 |
+
|
| 661 |
+
open Lean.Parser.Term in
|
| 662 |
+
/-- Return identifier names in the given bracketed binder. -/
|
| 663 |
+
def getBracketedBinderIds : Syntax β CommandElabM (Array Name)
|
| 664 |
+
| `(bracketedBinderF|($ids* $[: $ty?]? $(_annot?)?)) => return ids.map Syntax.getId
|
| 665 |
+
| `(bracketedBinderF|{$ids* $[: $ty?]?}) => return ids.map Syntax.getId
|
| 666 |
+
| `(bracketedBinderF|β¦$ids* : $_β¦) => return ids.map Syntax.getId
|
| 667 |
+
| `(bracketedBinderF|[$id : $_]) => return #[id.getId]
|
| 668 |
+
| `(bracketedBinderF|[$_]) => return #[Name.anonymous]
|
| 669 |
+
| _ => throwUnsupportedSyntax
|
| 670 |
+
|
| 671 |
+
private def mkTermContext (ctx : Context) (s : State) : CommandElabM Term.Context := do
|
| 672 |
+
let scope := s.scopes.head!
|
| 673 |
+
let mut sectionVars := {}
|
| 674 |
+
for id in (β scope.varDecls.flatMapM getBracketedBinderIds), uid in scope.varUIds do
|
| 675 |
+
sectionVars := sectionVars.insert id uid
|
| 676 |
+
return {
|
| 677 |
+
macroStack := ctx.macroStack
|
| 678 |
+
sectionVars := sectionVars
|
| 679 |
+
isNoncomputableSection := scope.isNoncomputable }
|
| 680 |
+
|
| 681 |
+
/--
|
| 682 |
+
Lift the `TermElabM` monadic action `x` into a `CommandElabM` monadic action.
|
| 683 |
+
|
| 684 |
+
Note that `x` is executed with an empty message log. Thus, `x` cannot modify/view messages produced by
|
| 685 |
+
previous commands.
|
| 686 |
+
|
| 687 |
+
If you need to access the free variables corresponding to the ones declared using the `variable` command,
|
| 688 |
+
consider using `runTermElabM`.
|
| 689 |
+
|
| 690 |
+
Recall that `TermElabM` actions can automatically lift `MetaM` and `CoreM` actions.
|
| 691 |
+
Example:
|
| 692 |
+
```
|
| 693 |
+
import Lean
|
| 694 |
+
|
| 695 |
+
open Lean Elab Command Meta
|
| 696 |
+
|
| 697 |
+
def printExpr (e : Expr) : MetaM Unit := do
|
| 698 |
+
IO.println s!"{β ppExpr e} : {β ppExpr (β inferType e)}"
|
| 699 |
+
|
| 700 |
+
#eval
|
| 701 |
+
liftTermElabM do
|
| 702 |
+
printExpr (mkConst ``Nat)
|
| 703 |
+
```
|
| 704 |
+
-/
|
| 705 |
+
def liftTermElabM (x : TermElabM Ξ±) : CommandElabM Ξ± := do
|
| 706 |
+
let ctx β read
|
| 707 |
+
let s β get
|
| 708 |
+
-- dbg_trace "heartbeats: {heartbeats}"
|
| 709 |
+
let scope := s.scopes.head!
|
| 710 |
+
-- We execute `x` with an empty message log. Thus, `x` cannot modify/view messages produced by previous commands.
|
| 711 |
+
-- This is useful for implementing `runTermElabM` where we use `Term.resetMessageLog`
|
| 712 |
+
let x : TermElabM _ := withSaveInfoContext x
|
| 713 |
+
-- make sure `observing` below also catches runtime exceptions (like we do by default in
|
| 714 |
+
-- `CommandElabM`)
|
| 715 |
+
let _ := MonadAlwaysExcept.except (m := TermElabM)
|
| 716 |
+
let x : MetaM _ := (observing (try x finally Meta.reportDiag)).run (β mkTermContext ctx s) { levelNames := scope.levelNames }
|
| 717 |
+
let x : CoreM _ := x.run mkMetaContext {}
|
| 718 |
+
let ((ea, _), _) β runCore x
|
| 719 |
+
MonadExcept.ofExcept ea
|
| 720 |
+
|
| 721 |
+
instance : MonadEval TermElabM CommandElabM where
|
| 722 |
+
monadEval := liftTermElabM
|
| 723 |
+
|
| 724 |
+
/--
|
| 725 |
+
Execute the monadic action `elabFn xs` as a `CommandElabM` monadic action, where `xs` are free variables
|
| 726 |
+
corresponding to all active scoped variables declared using the `variable` command.
|
| 727 |
+
|
| 728 |
+
This method is similar to `liftTermElabM`, but it elaborates all scoped variables declared using the `variable`
|
| 729 |
+
command.
|
| 730 |
+
|
| 731 |
+
Example:
|
| 732 |
+
```
|
| 733 |
+
import Lean
|
| 734 |
+
|
| 735 |
+
open Lean Elab Command Meta
|
| 736 |
+
|
| 737 |
+
variable {Ξ± : Type u} {f : Ξ± β Ξ±}
|
| 738 |
+
variable (n : Nat)
|
| 739 |
+
|
| 740 |
+
#eval
|
| 741 |
+
runTermElabM fun xs => do
|
| 742 |
+
for x in xs do
|
| 743 |
+
IO.println s!"{β ppExpr x} : {β ppExpr (β inferType x)}"
|
| 744 |
+
```
|
| 745 |
+
-/
|
| 746 |
+
def runTermElabM (elabFn : Array Expr β TermElabM Ξ±) : CommandElabM Ξ± := do
|
| 747 |
+
let scope β getScope
|
| 748 |
+
liftTermElabM <|
|
| 749 |
+
Term.withAutoBoundImplicit <|
|
| 750 |
+
Term.elabBinders scope.varDecls fun xs => do
|
| 751 |
+
-- We need to synthesize postponed terms because this is a checkpoint for the auto-bound implicit feature
|
| 752 |
+
-- If we don't use this checkpoint here, then auto-bound implicits in the postponed terms will not be handled correctly.
|
| 753 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 754 |
+
let mut sectionFVars := {}
|
| 755 |
+
for uid in scope.varUIds, x in xs do
|
| 756 |
+
sectionFVars := sectionFVars.insert uid x
|
| 757 |
+
withReader ({ Β· with sectionFVars := sectionFVars }) do
|
| 758 |
+
-- We don't want to store messages produced when elaborating `(getVarDecls s)` because they have already been saved when we elaborated the `variable`(s) command.
|
| 759 |
+
-- So, we use `Core.resetMessageLog`.
|
| 760 |
+
Core.resetMessageLog
|
| 761 |
+
let xs β Term.addAutoBoundImplicits xs none
|
| 762 |
+
if xs.all (Β·.isFVar) then
|
| 763 |
+
Term.withoutAutoBoundImplicit <| elabFn xs
|
| 764 |
+
else
|
| 765 |
+
-- Abstract any mvars that appear in `xs` using `mkForallFVars` (the type `mkSort levelZero` is an arbitrary placeholder)
|
| 766 |
+
-- and then rebuild the local context from scratch.
|
| 767 |
+
-- Resetting prevents the local context from including the original fvars from `xs`.
|
| 768 |
+
let ctxType β Meta.mkForallFVars' xs (mkSort levelZero)
|
| 769 |
+
Meta.withLCtx {} {} <| Meta.forallBoundedTelescope ctxType xs.size fun xs _ =>
|
| 770 |
+
Term.withoutAutoBoundImplicit <| elabFn xs
|
| 771 |
+
|
| 772 |
+
private def liftAttrM {Ξ±} (x : AttrM Ξ±) : CommandElabM Ξ± := do
|
| 773 |
+
liftCoreM x
|
| 774 |
+
|
| 775 |
+
/--
|
| 776 |
+
Return the stack of all currently active scopes:
|
| 777 |
+
the base scope always comes last; new scopes are prepended in the front.
|
| 778 |
+
In particular, the current scope is always the first element.
|
| 779 |
+
-/
|
| 780 |
+
def getScopes : CommandElabM (List Scope) := do
|
| 781 |
+
pure (β get).scopes
|
| 782 |
+
|
| 783 |
+
def modifyScope (f : Scope β Scope) : CommandElabM Unit :=
|
| 784 |
+
modify fun s => { s with
|
| 785 |
+
scopes := match s.scopes with
|
| 786 |
+
| h::t => f h :: t
|
| 787 |
+
| [] => unreachable!
|
| 788 |
+
}
|
| 789 |
+
|
| 790 |
+
def withScope (f : Scope β Scope) (x : CommandElabM Ξ±) : CommandElabM Ξ± := do
|
| 791 |
+
match (β get).scopes with
|
| 792 |
+
| [] => x
|
| 793 |
+
| h :: t =>
|
| 794 |
+
try
|
| 795 |
+
modify fun s => { s with scopes := f h :: t }
|
| 796 |
+
x
|
| 797 |
+
finally
|
| 798 |
+
modify fun s => { s with scopes := h :: t }
|
| 799 |
+
|
| 800 |
+
def getLevelNames : CommandElabM (List Name) :=
|
| 801 |
+
return (β getScope).levelNames
|
| 802 |
+
|
| 803 |
+
def addUnivLevel (idStx : Syntax) : CommandElabM Unit := withRef idStx do
|
| 804 |
+
let id := idStx.getId
|
| 805 |
+
let levelNames β getLevelNames
|
| 806 |
+
if levelNames.elem id then
|
| 807 |
+
throwAlreadyDeclaredUniverseLevel id
|
| 808 |
+
else
|
| 809 |
+
modifyScope fun scope => { scope with levelNames := id :: scope.levelNames }
|
| 810 |
+
|
| 811 |
+
end Elab.Command
|
| 812 |
+
|
| 813 |
+
open Elab Command MonadRecDepth
|
| 814 |
+
|
| 815 |
+
private def liftCommandElabMCore (cmd : CommandElabM Ξ±) (throwOnError : Bool) : CoreM Ξ± := do
|
| 816 |
+
let s : Core.State β get
|
| 817 |
+
let ctx : Core.Context β read
|
| 818 |
+
let (a, commandState) β
|
| 819 |
+
cmd.run {
|
| 820 |
+
fileName := ctx.fileName
|
| 821 |
+
fileMap := ctx.fileMap
|
| 822 |
+
currRecDepth := ctx.currRecDepth
|
| 823 |
+
currMacroScope := ctx.currMacroScope
|
| 824 |
+
ref := ctx.ref
|
| 825 |
+
snap? := none
|
| 826 |
+
cancelTk? := ctx.cancelTk?
|
| 827 |
+
suppressElabErrors := ctx.suppressElabErrors
|
| 828 |
+
} |>.run {
|
| 829 |
+
env := s.env
|
| 830 |
+
nextMacroScope := s.nextMacroScope
|
| 831 |
+
maxRecDepth := ctx.maxRecDepth
|
| 832 |
+
ngen := s.ngen
|
| 833 |
+
auxDeclNGen := s.auxDeclNGen
|
| 834 |
+
scopes := [{ header := "", opts := ctx.options }]
|
| 835 |
+
infoState.enabled := s.infoState.enabled
|
| 836 |
+
}
|
| 837 |
+
modify fun coreState => { coreState with
|
| 838 |
+
env := commandState.env
|
| 839 |
+
nextMacroScope := commandState.nextMacroScope
|
| 840 |
+
ngen := commandState.ngen
|
| 841 |
+
auxDeclNGen := commandState.auxDeclNGen
|
| 842 |
+
traceState.traces := coreState.traceState.traces ++ commandState.traceState.traces
|
| 843 |
+
}
|
| 844 |
+
if throwOnError then
|
| 845 |
+
if let some err := commandState.messages.toArray.find? (Β·.severity matches .error) then
|
| 846 |
+
throwError err.data
|
| 847 |
+
modify fun coreState => { coreState with
|
| 848 |
+
infoState.trees := coreState.infoState.trees.append commandState.infoState.trees
|
| 849 |
+
messages := coreState.messages ++ commandState.messages
|
| 850 |
+
}
|
| 851 |
+
return a
|
| 852 |
+
|
| 853 |
+
/--
|
| 854 |
+
Lifts an action in `CommandElabM` into `CoreM`, updating the environment,
|
| 855 |
+
messages, info trees, traces, the name generator, and macro scopes.
|
| 856 |
+
The action is run in a context with an empty message log, empty trace state, and empty info trees.
|
| 857 |
+
|
| 858 |
+
If `throwOnError` is true, then if the command produces an error message, it is converted into an exception.
|
| 859 |
+
In this case, info trees and messages are not carried over.
|
| 860 |
+
|
| 861 |
+
Commands that modify the processing of subsequent commands,
|
| 862 |
+
such as `open` and `namespace` commands,
|
| 863 |
+
only have an effect for the remainder of the `CommandElabM` computation passed here,
|
| 864 |
+
and do not affect subsequent commands.
|
| 865 |
+
|
| 866 |
+
*Warning:* when using this from `MetaM` monads, the caches are *not* reset.
|
| 867 |
+
If the command defines new instances for example, you should use `Lean.Meta.resetSynthInstanceCache`
|
| 868 |
+
to reset the instance cache.
|
| 869 |
+
While the `modifyEnv` function for `MetaM` clears its caches entirely,
|
| 870 |
+
`liftCommandElabM` has no way to reset these caches.
|
| 871 |
+
-/
|
| 872 |
+
def liftCommandElabM (cmd : CommandElabM Ξ±) (throwOnError : Bool := true) : CoreM Ξ± := do
|
| 873 |
+
-- `observing` ensures that if `cmd` throws an exception we still thread state back to `CoreM`.
|
| 874 |
+
MonadExcept.ofExcept (β liftCommandElabMCore (observing cmd) throwOnError)
|
| 875 |
+
|
| 876 |
+
/--
|
| 877 |
+
Given a command elaborator `cmd`, returns a new command elaborator that
|
| 878 |
+
first evaluates any local `set_option ... in ...` clauses and then invokes `cmd` on what remains.
|
| 879 |
+
-/
|
| 880 |
+
partial def withSetOptionIn (cmd : CommandElab) : CommandElab := fun stx => do
|
| 881 |
+
if stx.getKind == ``Lean.Parser.Command.in &&
|
| 882 |
+
stx[0].getKind == ``Lean.Parser.Command.set_option then
|
| 883 |
+
let opts β Elab.elabSetOption stx[0][1] stx[0][3]
|
| 884 |
+
Command.withScope (fun scope => { scope with opts }) do
|
| 885 |
+
withSetOptionIn cmd stx[2]
|
| 886 |
+
else
|
| 887 |
+
cmd stx
|
| 888 |
+
|
| 889 |
+
export Elab.Command (Linter addLinter)
|
| 890 |
+
|
| 891 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/ComputedFields.lean
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Gabriel Ebner
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Constructions.CasesOn
|
| 8 |
+
import Lean.Compiler.ImplementedByAttr
|
| 9 |
+
import Lean.Elab.PreDefinition.WF.Eqns
|
| 10 |
+
|
| 11 |
+
/-!
|
| 12 |
+
# Computed fields
|
| 13 |
+
|
| 14 |
+
Inductives can have computed fields which are recursive functions whose value
|
| 15 |
+
is stored in the constructors, and can be accessed in constant time.
|
| 16 |
+
|
| 17 |
+
```lean
|
| 18 |
+
inductive Exp
|
| 19 |
+
| hole
|
| 20 |
+
| app (x y : Exp)
|
| 21 |
+
with
|
| 22 |
+
f : Exp β Nat
|
| 23 |
+
| .hole => 42
|
| 24 |
+
| .app x y => f x + f y
|
| 25 |
+
|
| 26 |
+
-- `Exp.f x` runs in constant time, even if `x` is a dag-like value
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
This file implements the computed fields feature by simulating it via
|
| 30 |
+
`implemented_by`. The main function is `setComputedFields`.
|
| 31 |
+
-/
|
| 32 |
+
|
| 33 |
+
namespace Lean.Elab.ComputedFields
|
| 34 |
+
open Meta
|
| 35 |
+
|
| 36 |
+
/--
|
| 37 |
+
Marks a function as a computed field of an inductive.
|
| 38 |
+
|
| 39 |
+
Computed fields are specified in the with-block of an inductive type declaration. They can be used
|
| 40 |
+
to allow certain values to be computed only once at the time of construction and then later be
|
| 41 |
+
accessed immediately.
|
| 42 |
+
|
| 43 |
+
Example:
|
| 44 |
+
```
|
| 45 |
+
inductive NatList where
|
| 46 |
+
| nil
|
| 47 |
+
| cons : Nat β NatList β NatList
|
| 48 |
+
with
|
| 49 |
+
@[computed_field] sum : NatList β Nat
|
| 50 |
+
| .nil => 0
|
| 51 |
+
| .cons x l => x + l.sum
|
| 52 |
+
@[computed_field] length : NatList β Nat
|
| 53 |
+
| .nil => 0
|
| 54 |
+
| .cons _ l => l.length + 1
|
| 55 |
+
```
|
| 56 |
+
-/
|
| 57 |
+
@[builtin_doc]
|
| 58 |
+
builtin_initialize computedFieldAttr : TagAttribute β
|
| 59 |
+
registerTagAttribute `computed_field "Marks a function as a computed field of an inductive" fun _ => do
|
| 60 |
+
unless (β getOptions).getBool `elaboratingComputedFields do
|
| 61 |
+
throwError "The @[computed_field] attribute can only be used in the with-block of an inductive"
|
| 62 |
+
|
| 63 |
+
def mkUnsafeCastTo (expectedType : Expr) (e : Expr) : MetaM Expr :=
|
| 64 |
+
mkAppOptM ``unsafeCast #[none, expectedType, e]
|
| 65 |
+
|
| 66 |
+
def isScalarField (ctor : Name) : CoreM Bool :=
|
| 67 |
+
return (β getConstInfoCtor ctor).numFields == 0 -- TODO
|
| 68 |
+
|
| 69 |
+
structure Context extends InductiveVal where
|
| 70 |
+
lparams : List Level
|
| 71 |
+
params : Array Expr
|
| 72 |
+
compFields : Array Name
|
| 73 |
+
compFieldVars : Array Expr
|
| 74 |
+
indices : Array Expr
|
| 75 |
+
val : Expr
|
| 76 |
+
|
| 77 |
+
abbrev M := ReaderT Context MetaM
|
| 78 |
+
|
| 79 |
+
-- TODO: doesn't work if match contains patterns like `.app (.app a b) c`
|
| 80 |
+
def getComputedFieldValue (computedField : Name) (ctorTerm : Expr) : MetaM Expr := do
|
| 81 |
+
let ctorName := ctorTerm.getAppFn.constName!
|
| 82 |
+
let ind β getConstInfoInduct (β getConstInfoCtor ctorName).induct
|
| 83 |
+
let val β mkAppOptM computedField (.replicate (ind.numParams+ind.numIndices) none ++ #[some ctorTerm])
|
| 84 |
+
let val β
|
| 85 |
+
if let some wfEqn := WF.eqnInfoExt.find? (β getEnv) computedField then
|
| 86 |
+
pure <| mkAppN (wfEqn.value.instantiateLevelParams wfEqn.levelParams val.getAppFn.constLevels!) val.getAppArgs
|
| 87 |
+
else
|
| 88 |
+
unfoldDefinition val
|
| 89 |
+
let val β whnfHeadPred val (return ctorTerm.occurs Β·)
|
| 90 |
+
if !ctorTerm.occurs val then return val
|
| 91 |
+
throwError "computed field {computedField} does not reduce for constructor {ctorName}"
|
| 92 |
+
|
| 93 |
+
def validateComputedFields : M Unit := do
|
| 94 |
+
let {compFieldVars, indices, val ..} β read
|
| 95 |
+
for cf in compFieldVars do
|
| 96 |
+
let ty β inferType cf
|
| 97 |
+
if ty.containsFVar val.fvarId! then
|
| 98 |
+
throwError "computed field {cf}'s type must not depend on value{indentExpr ty}"
|
| 99 |
+
if indices.any (ty.containsFVar Β·.fvarId!) then
|
| 100 |
+
throwError "computed field {cf}'s type must not depend on indices{indentExpr ty}"
|
| 101 |
+
|
| 102 |
+
def mkImplType : M Unit := do
|
| 103 |
+
let {name, isUnsafe, type, ctors, levelParams, numParams, lparams, params, compFieldVars, ..} β read
|
| 104 |
+
addDecl <| .inductDecl levelParams numParams
|
| 105 |
+
(isUnsafe := isUnsafe) -- Note: inlining is disabled with unsafe inductives
|
| 106 |
+
[{ name := name ++ `_impl, type,
|
| 107 |
+
ctors := β ctors.mapM fun ctor => do
|
| 108 |
+
forallTelescope (β inferType (mkAppN (mkConst ctor lparams) params)) fun fields retTy => do
|
| 109 |
+
let retTy := mkAppN (mkConst (name ++ `_impl) lparams) retTy.getAppArgs
|
| 110 |
+
let type β mkForallFVars (params ++ (if β isScalarField ctor then #[] else compFieldVars) ++ fields) retTy
|
| 111 |
+
return { name := ctor ++ `_impl, type } }]
|
| 112 |
+
|
| 113 |
+
def overrideCasesOn : M Unit := do
|
| 114 |
+
let {name, numIndices, ctors, lparams, params, compFieldVars, ..} β read
|
| 115 |
+
let casesOn β getConstInfoDefn (mkCasesOnName name)
|
| 116 |
+
mkCasesOn (name ++ `_impl)
|
| 117 |
+
let value β
|
| 118 |
+
forallTelescope (β instantiateForall casesOn.type params) fun xs constMotive => do
|
| 119 |
+
let (indices, major, minors) := (xs[1...=numIndices].toArray,
|
| 120 |
+
xs[numIndices+1]!, xs[(numIndices+2)...*].toArray)
|
| 121 |
+
let majorImplTy := mkAppN (mkConst (name ++ `_impl) lparams) (params ++ indices)
|
| 122 |
+
mkLambdaFVars (params ++ xs) <|
|
| 123 |
+
mkAppN (mkConst (mkCasesOnName (name ++ `_impl))
|
| 124 |
+
(casesOn.levelParams.map mkLevelParam)) <|
|
| 125 |
+
params ++
|
| 126 |
+
#[β withLocalDeclD `a majorImplTy fun majorImpl => do
|
| 127 |
+
withLetDecl `m (β inferType constMotive) constMotive fun m => do
|
| 128 |
+
mkLambdaFVars (#[m] ++ indices ++ #[majorImpl]) m] ++
|
| 129 |
+
indices ++ #[β mkUnsafeCastTo majorImplTy major] ++
|
| 130 |
+
(β (minors.zip ctors.toArray).mapM fun (minor, ctor) => do
|
| 131 |
+
forallTelescope (β inferType minor) fun args _ => do
|
| 132 |
+
mkLambdaFVars ((if β isScalarField ctor then #[] else compFieldVars) ++ args)
|
| 133 |
+
(β mkUnsafeCastTo constMotive (mkAppN minor args)))
|
| 134 |
+
let nameOverride := mkCasesOnName name ++ `_override
|
| 135 |
+
addDecl <| .defnDecl { casesOn with
|
| 136 |
+
name := nameOverride
|
| 137 |
+
all := [nameOverride]
|
| 138 |
+
value
|
| 139 |
+
hints := .opaque
|
| 140 |
+
safety := .unsafe
|
| 141 |
+
}
|
| 142 |
+
setInlineAttribute (mkCasesOnName name ++ `_override)
|
| 143 |
+
setImplementedBy (mkCasesOnName name) (mkCasesOnName name ++ `_override)
|
| 144 |
+
|
| 145 |
+
def overrideConstructors : M Unit := do
|
| 146 |
+
let {ctors, levelParams, lparams, params, compFields, ..} β read
|
| 147 |
+
for ctor in ctors do
|
| 148 |
+
forallTelescope (β inferType (mkAppN (mkConst ctor lparams) params)) fun fields retTy => do
|
| 149 |
+
let ctorTerm := mkAppN (mkConst ctor lparams) (params ++ fields)
|
| 150 |
+
let computedFieldVals β
|
| 151 |
+
-- elaborating a non-exposed def body
|
| 152 |
+
withoutExporting do
|
| 153 |
+
if β isScalarField ctor then pure #[] else
|
| 154 |
+
compFields.mapM (getComputedFieldValue Β· ctorTerm)
|
| 155 |
+
addDecl <| .defnDecl {
|
| 156 |
+
name := ctor ++ `_override
|
| 157 |
+
levelParams
|
| 158 |
+
type := β inferType (mkConst ctor lparams)
|
| 159 |
+
value := β mkLambdaFVars (params ++ fields) <| β mkUnsafeCastTo retTy <|
|
| 160 |
+
mkAppN (mkConst (ctor ++ `_impl) lparams) (params ++ computedFieldVals ++ fields)
|
| 161 |
+
hints := .opaque
|
| 162 |
+
safety := .unsafe
|
| 163 |
+
}
|
| 164 |
+
setImplementedBy ctor (ctor ++ `_override)
|
| 165 |
+
if β isScalarField ctor then setInlineAttribute (ctor ++ `_override)
|
| 166 |
+
|
| 167 |
+
def overrideComputedFields : M Unit := do
|
| 168 |
+
let {name, levelParams, ctors, compFields, compFieldVars, lparams, params, indices, val ..} β read
|
| 169 |
+
withLocalDeclD `x (mkAppN (mkConst (name ++ `_impl) lparams) (params ++ indices)) fun xImpl => do
|
| 170 |
+
for cfn in compFields, cf in compFieldVars do
|
| 171 |
+
if isExtern (β getEnv) cfn then
|
| 172 |
+
compileDecls [cfn]
|
| 173 |
+
continue
|
| 174 |
+
let cases β
|
| 175 |
+
-- elaborating a non-exposed def body
|
| 176 |
+
withoutExporting do
|
| 177 |
+
ctors.toArray.mapM fun ctor => do
|
| 178 |
+
forallTelescope (β inferType (mkAppN (mkConst ctor lparams) params)) fun fields _ => do
|
| 179 |
+
if β isScalarField ctor then
|
| 180 |
+
mkLambdaFVars fields <|
|
| 181 |
+
β getComputedFieldValue cfn (mkAppN (mkConst ctor lparams) (params ++ fields))
|
| 182 |
+
else
|
| 183 |
+
mkLambdaFVars (compFieldVars ++ fields) cf
|
| 184 |
+
addDecl <| .defnDecl {
|
| 185 |
+
name := cfn ++ `_override
|
| 186 |
+
levelParams
|
| 187 |
+
type := β mkForallFVars (params ++ indices ++ #[val]) (β inferType cf)
|
| 188 |
+
value := β mkLambdaFVars (params ++ indices ++ #[val]) <|
|
| 189 |
+
β mkAppOptM (mkCasesOnName (name ++ `_impl))
|
| 190 |
+
((params ++ #[β mkLambdaFVars (indices.push xImpl) (β inferType cf)] ++ indices ++
|
| 191 |
+
#[β mkUnsafeCastTo (β inferType xImpl) val] ++ cases).map some)
|
| 192 |
+
safety := .unsafe
|
| 193 |
+
hints := .opaque
|
| 194 |
+
}
|
| 195 |
+
setImplementedBy cfn (cfn ++ `_override)
|
| 196 |
+
|
| 197 |
+
def mkComputedFieldOverrides (declName : Name) (compFields : Array Name) : MetaM Unit := do
|
| 198 |
+
let ind β getConstInfoInduct declName
|
| 199 |
+
if ind.ctors.length < 2 then
|
| 200 |
+
throwError "computed fields require at least two constructors"
|
| 201 |
+
let lparams := ind.levelParams.map mkLevelParam
|
| 202 |
+
forallTelescope ind.type fun paramsIndices _ => do
|
| 203 |
+
withLocalDeclD `x (mkAppN (mkConst ind.name lparams) paramsIndices) fun val => do
|
| 204 |
+
let params := paramsIndices[*...ind.numParams].toArray
|
| 205 |
+
let indices := paramsIndices[ind.numParams...*].toArray
|
| 206 |
+
let compFieldVars := compFields.map fun fieldDeclName =>
|
| 207 |
+
(fieldDeclName.updatePrefix .anonymous,
|
| 208 |
+
fun _ => do inferType (β mkAppM fieldDeclName (params ++ indices ++ #[val])))
|
| 209 |
+
withLocalDeclsD compFieldVars fun compFieldVars => do
|
| 210 |
+
let ctx := { ind with lparams, params, compFields, compFieldVars, indices, val }
|
| 211 |
+
ReaderT.run (r := ctx) do
|
| 212 |
+
validateComputedFields
|
| 213 |
+
mkImplType
|
| 214 |
+
overrideCasesOn
|
| 215 |
+
overrideConstructors
|
| 216 |
+
overrideComputedFields
|
| 217 |
+
|
| 218 |
+
/--
|
| 219 |
+
Sets the computed fields for a block of mutual inductives,
|
| 220 |
+
adding the implementation via `implemented_by`.
|
| 221 |
+
|
| 222 |
+
The `computedFields` argument contains a pair
|
| 223 |
+
for every inductive in the mutual block,
|
| 224 |
+
consisting of the name of the inductive
|
| 225 |
+
and the names of the associated computed fields.
|
| 226 |
+
-/
|
| 227 |
+
def setComputedFields (computedFields : Array (Name Γ Array Name)) : MetaM Unit := do
|
| 228 |
+
for (indName, computedFieldNames) in computedFields do
|
| 229 |
+
for computedFieldName in computedFieldNames do
|
| 230 |
+
unless computedFieldAttr.hasTag (β getEnv) computedFieldName do
|
| 231 |
+
logError m!"'{computedFieldName}' must be tagged with @[computed_field]"
|
| 232 |
+
mkComputedFieldOverrides indName computedFieldNames
|
| 233 |
+
|
| 234 |
+
-- Once all the implemented_by infrastructure is set up, compile everything.
|
| 235 |
+
compileDecls <| computedFields.toList.map fun (indName, _) =>
|
| 236 |
+
mkCasesOnName indName ++ `_override
|
| 237 |
+
|
| 238 |
+
let mut toCompile := #[]
|
| 239 |
+
for (declName, computedFields) in computedFields do
|
| 240 |
+
let ind β getConstInfoInduct declName
|
| 241 |
+
for ctor in ind.ctors do
|
| 242 |
+
toCompile := toCompile.push (ctor ++ `_override)
|
| 243 |
+
for fieldName in computedFields do
|
| 244 |
+
unless isExtern (β getEnv) fieldName do
|
| 245 |
+
toCompile := toCompile.push <| fieldName ++ `_override
|
| 246 |
+
compileDecls toCompile.toList
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Config.lean
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.Elab.Term
|
| 10 |
+
|
| 11 |
+
/--
|
| 12 |
+
Set `isDefEq` configuration for the elaborator.
|
| 13 |
+
Note that we enable all approximations but `quasiPatternApprox`
|
| 14 |
+
|
| 15 |
+
In Lean3 and Lean 4, we used to use the quasi-pattern approximation during elaboration.
|
| 16 |
+
The example:
|
| 17 |
+
```
|
| 18 |
+
def ex : StateT Ξ΄ (StateT Ο Id) Ο :=
|
| 19 |
+
monadLift (get : StateT Ο Id Ο)
|
| 20 |
+
```
|
| 21 |
+
demonstrates why it produces counterintuitive behavior.
|
| 22 |
+
We have the `Monad-lift` application:
|
| 23 |
+
```
|
| 24 |
+
@monadLift ?m ?n ?c ?Ξ± (get : StateT Ο id Ο) : ?n ?Ξ±
|
| 25 |
+
```
|
| 26 |
+
It produces the following unification problem when we process the expected type:
|
| 27 |
+
```
|
| 28 |
+
?n ?Ξ± =?= StateT Ξ΄ (StateT Ο id) Ο
|
| 29 |
+
==> (approximate using first-order unification)
|
| 30 |
+
?n := StateT Ξ΄ (StateT Ο id)
|
| 31 |
+
?Ξ± := Ο
|
| 32 |
+
```
|
| 33 |
+
Then, we need to solve:
|
| 34 |
+
```
|
| 35 |
+
?m ?Ξ± =?= StateT Ο id Ο
|
| 36 |
+
==> instantiate metavars
|
| 37 |
+
?m Ο =?= StateT Ο id Ο
|
| 38 |
+
==> (approximate since it is a quasi-pattern unification constraint)
|
| 39 |
+
?m := fun Ο => StateT Ο id Ο
|
| 40 |
+
```
|
| 41 |
+
Note that the constraint is not a Milner pattern because Ο is in
|
| 42 |
+
the local context of `?m`. We are ignoring the other possible solutions:
|
| 43 |
+
```
|
| 44 |
+
?m := fun Ο' => StateT Ο id Ο
|
| 45 |
+
?m := fun Ο' => StateT Ο' id Ο
|
| 46 |
+
?m := fun Ο' => StateT Ο id Ο'
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
We need the quasi-pattern approximation for elaborating recursor-like expressions (e.g., dependent `match with` expressions).
|
| 50 |
+
|
| 51 |
+
If we had use first-order unification, then we would have produced
|
| 52 |
+
the right answer: `?m := StateT Ο id`
|
| 53 |
+
|
| 54 |
+
Haskell would work on this example since it always uses
|
| 55 |
+
first-order unification.
|
| 56 |
+
-/
|
| 57 |
+
def setElabConfig (cfg : Meta.Config) : Meta.Config :=
|
| 58 |
+
{ cfg with foApprox := true, ctxApprox := true, constApprox := false, quasiPatternApprox := false }
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclModifiers.lean
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Structure
|
| 8 |
+
import Lean.Elab.Attributes
|
| 9 |
+
import Lean.DocString.Add
|
| 10 |
+
|
| 11 |
+
namespace Lean.Elab
|
| 12 |
+
|
| 13 |
+
/--
|
| 14 |
+
Ensure the environment does not contain a declaration with name `declName`.
|
| 15 |
+
Recall that a private declaration cannot shadow a non-private one and vice-versa, although
|
| 16 |
+
they internally have different names.
|
| 17 |
+
-/
|
| 18 |
+
def checkNotAlreadyDeclared {m} [Monad m] [MonadEnv m] [MonadError m] [MonadInfoTree m] (declName : Name) : m Unit := do
|
| 19 |
+
let env β getEnv
|
| 20 |
+
let addInfo declName := do
|
| 21 |
+
pushInfoLeaf <| .ofTermInfo {
|
| 22 |
+
elaborator := .anonymous, lctx := {}, expectedType? := none
|
| 23 |
+
stx := (β getRef)
|
| 24 |
+
expr := (β mkConstWithLevelParams declName)
|
| 25 |
+
}
|
| 26 |
+
if env.contains declName then
|
| 27 |
+
addInfo declName
|
| 28 |
+
match privateToUserName? declName with
|
| 29 |
+
| none => throwError "'{.ofConstName declName true}' has already been declared"
|
| 30 |
+
| some declName => throwError "private declaration '{.ofConstName declName true}' has already been declared"
|
| 31 |
+
if isReservedName env (privateToUserName declName) || isReservedName env (mkPrivateName (β getEnv) declName) then
|
| 32 |
+
throwError "'{declName}' is a reserved name"
|
| 33 |
+
if env.contains (mkPrivateName env declName) then
|
| 34 |
+
addInfo (mkPrivateName env declName)
|
| 35 |
+
throwError "a private declaration '{.ofConstName declName true}' has already been declared"
|
| 36 |
+
match privateToUserName? declName with
|
| 37 |
+
| none => pure ()
|
| 38 |
+
| some declName =>
|
| 39 |
+
if env.contains declName then
|
| 40 |
+
addInfo declName
|
| 41 |
+
throwError "a non-private declaration '{.ofConstName declName true}' has already been declared"
|
| 42 |
+
|
| 43 |
+
/-- Declaration visibility modifier. That is, whether a declaration is regular, protected or private. -/
|
| 44 |
+
inductive Visibility where
|
| 45 |
+
| regular | Β«protectedΒ» | Β«privateΒ» | Β«publicΒ»
|
| 46 |
+
deriving Inhabited
|
| 47 |
+
|
| 48 |
+
instance : ToString Visibility where
|
| 49 |
+
toString
|
| 50 |
+
| .regular => "regular"
|
| 51 |
+
| .private => "private"
|
| 52 |
+
| .protected => "protected"
|
| 53 |
+
| .public => "public"
|
| 54 |
+
|
| 55 |
+
def Visibility.isPrivate : Visibility β Bool
|
| 56 |
+
| .private => true
|
| 57 |
+
| _ => false
|
| 58 |
+
|
| 59 |
+
def Visibility.isProtected : Visibility β Bool
|
| 60 |
+
| .protected => true
|
| 61 |
+
| _ => false
|
| 62 |
+
|
| 63 |
+
def Visibility.isPublic : Visibility β Bool
|
| 64 |
+
| .public => true
|
| 65 |
+
| _ => false
|
| 66 |
+
|
| 67 |
+
def Visibility.isInferredPublic (env : Environment) (v : Visibility) : Bool :=
|
| 68 |
+
if env.isExporting || !env.header.isModule then !v.isPrivate else v.isPublic
|
| 69 |
+
|
| 70 |
+
/-- Whether a declaration is default, partial or nonrec. -/
|
| 71 |
+
inductive RecKind where
|
| 72 |
+
| Β«partialΒ» | Β«nonrecΒ» | default
|
| 73 |
+
deriving Inhabited
|
| 74 |
+
|
| 75 |
+
/-- Codegen-relevant modifiers. -/
|
| 76 |
+
inductive ComputeKind where
|
| 77 |
+
| regular | Β«metaΒ» | Β«noncomputableΒ»
|
| 78 |
+
deriving Inhabited
|
| 79 |
+
|
| 80 |
+
/-- Flags and data added to declarations (eg docstrings, attributes, `private`, `unsafe`, `partial`, ...). -/
|
| 81 |
+
structure Modifiers where
|
| 82 |
+
/-- Input syntax, used for adjusting declaration range (unless missing) -/
|
| 83 |
+
stx : TSyntax ``Parser.Command.declModifiers := β¨.missingβ©
|
| 84 |
+
docString? : Option (TSyntax ``Parser.Command.docComment) := none
|
| 85 |
+
visibility : Visibility := Visibility.regular
|
| 86 |
+
computeKind : ComputeKind := .regular
|
| 87 |
+
recKind : RecKind := RecKind.default
|
| 88 |
+
isUnsafe : Bool := false
|
| 89 |
+
attrs : Array Attribute := #[]
|
| 90 |
+
deriving Inhabited
|
| 91 |
+
|
| 92 |
+
def Modifiers.isPrivate (m : Modifiers) : Bool := m.visibility.isPrivate
|
| 93 |
+
def Modifiers.isProtected (m : Modifiers) : Bool := m.visibility.isProtected
|
| 94 |
+
def Modifiers.isPublic (m : Modifiers) : Bool := m.visibility.isPublic
|
| 95 |
+
def Modifiers.isInferredPublic (env : Environment) (m : Modifiers) : Bool :=
|
| 96 |
+
m.visibility.isInferredPublic env
|
| 97 |
+
|
| 98 |
+
def Modifiers.isPartial : Modifiers β Bool
|
| 99 |
+
| { recKind := .partial, .. } => true
|
| 100 |
+
| _ => false
|
| 101 |
+
|
| 102 |
+
/--
|
| 103 |
+
Whether the declaration is explicitly `partial` or should be considered as such via `meta`. In the
|
| 104 |
+
latter case, elaborators should not produce an error if partialty is unnecessary.
|
| 105 |
+
-/
|
| 106 |
+
def Modifiers.isInferredPartial : Modifiers β Bool
|
| 107 |
+
| { recKind := .partial, .. } => true
|
| 108 |
+
| { computeKind := .meta, .. } => true
|
| 109 |
+
| _ => false
|
| 110 |
+
|
| 111 |
+
def Modifiers.isNonrec : Modifiers β Bool
|
| 112 |
+
| { recKind := .nonrec, .. } => true
|
| 113 |
+
| _ => false
|
| 114 |
+
|
| 115 |
+
def Modifiers.isMeta (m : Modifiers) : Bool :=
|
| 116 |
+
m.computeKind matches .meta
|
| 117 |
+
|
| 118 |
+
def Modifiers.isNoncomputable (m : Modifiers) : Bool :=
|
| 119 |
+
m.computeKind matches .noncomputable
|
| 120 |
+
|
| 121 |
+
/-- Adds attribute `attr` in `modifiers` -/
|
| 122 |
+
def Modifiers.addAttr (modifiers : Modifiers) (attr : Attribute) : Modifiers :=
|
| 123 |
+
{ modifiers with attrs := modifiers.attrs.push attr }
|
| 124 |
+
|
| 125 |
+
/-- Adds attribute `attr` in `modifiers`, at the beginning -/
|
| 126 |
+
def Modifiers.addFirstAttr (modifiers : Modifiers) (attr : Attribute) : Modifiers :=
|
| 127 |
+
{ modifiers with attrs := #[attr] ++ modifiers.attrs }
|
| 128 |
+
|
| 129 |
+
/-- Filters attributes using `p` -/
|
| 130 |
+
def Modifiers.filterAttrs (modifiers : Modifiers) (p : Attribute β Bool) : Modifiers :=
|
| 131 |
+
{ modifiers with attrs := modifiers.attrs.filter p }
|
| 132 |
+
|
| 133 |
+
instance : ToFormat Modifiers := β¨fun m =>
|
| 134 |
+
let components : List Format :=
|
| 135 |
+
(match m.docString? with
|
| 136 |
+
| some str => [f!"/--{str}-/"]
|
| 137 |
+
| none => [])
|
| 138 |
+
++ (match m.visibility with
|
| 139 |
+
| .regular => []
|
| 140 |
+
| .private => [f!"private"]
|
| 141 |
+
| .protected => [f!"protected"]
|
| 142 |
+
| .public => [f!"public"])
|
| 143 |
+
++ (match m.computeKind with | .regular => [] | .meta => [f!"meta"] | .noncomputable => [f!"noncomputable"])
|
| 144 |
+
++ (match m.recKind with | RecKind.partial => [f!"partial"] | RecKind.nonrec => [f!"nonrec"] | _ => [])
|
| 145 |
+
++ (if m.isUnsafe then [f!"unsafe"] else [])
|
| 146 |
+
++ m.attrs.toList.map (fun attr => format attr)
|
| 147 |
+
Format.bracket "{" (Format.joinSep components ("," ++ Format.line)) "}"β©
|
| 148 |
+
|
| 149 |
+
instance : ToString Modifiers := β¨toString β formatβ©
|
| 150 |
+
|
| 151 |
+
/--
|
| 152 |
+
Retrieve doc string from `stx` of the form `(docComment)?`.
|
| 153 |
+
-/
|
| 154 |
+
def expandOptDocComment? [Monad m] [MonadError m] (optDocComment : Syntax) : m (Option String) :=
|
| 155 |
+
match optDocComment.getOptional? with
|
| 156 |
+
| none => return none
|
| 157 |
+
| some s => match s[1] with
|
| 158 |
+
| .atom _ val => return some (val.extract 0 (val.endPos - β¨2β©))
|
| 159 |
+
| _ => throwErrorAt s "unexpected doc string{indentD s[1]}"
|
| 160 |
+
|
| 161 |
+
section Methods
|
| 162 |
+
|
| 163 |
+
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]
|
| 164 |
+
|
| 165 |
+
/-- Elaborate declaration modifiers (i.e., attributes, `partial`, `private`, `protected`, `unsafe`, `meta`, `noncomputable`, doc string)-/
|
| 166 |
+
def elabModifiers (stx : TSyntax ``Parser.Command.declModifiers) : m Modifiers := do
|
| 167 |
+
let docCommentStx := stx.raw[0]
|
| 168 |
+
let attrsStx := stx.raw[1]
|
| 169 |
+
let visibilityStx := stx.raw[2]
|
| 170 |
+
let computeKind :=
|
| 171 |
+
if stx.raw[3].isNone then
|
| 172 |
+
.regular
|
| 173 |
+
else if stx.raw[3][0].getKind == ``Parser.Command.meta then
|
| 174 |
+
.meta
|
| 175 |
+
else
|
| 176 |
+
.noncomputable
|
| 177 |
+
let unsafeStx := stx.raw[4]
|
| 178 |
+
let recKind :=
|
| 179 |
+
if stx.raw[5].isNone then
|
| 180 |
+
RecKind.default
|
| 181 |
+
else if stx.raw[5][0].getKind == ``Parser.Command.partial then
|
| 182 |
+
RecKind.partial
|
| 183 |
+
else
|
| 184 |
+
RecKind.nonrec
|
| 185 |
+
let docString? := docCommentStx.getOptional?.map TSyntax.mk
|
| 186 |
+
let visibility β match visibilityStx.getOptional? with
|
| 187 |
+
| none => pure .regular
|
| 188 |
+
| some v =>
|
| 189 |
+
match v with
|
| 190 |
+
| `(Parser.Command.visibility| private) => pure .private
|
| 191 |
+
| `(Parser.Command.visibility| protected) => pure .protected
|
| 192 |
+
| `(Parser.Command.visibility| public) => pure .public
|
| 193 |
+
| _ => throwErrorAt v "unexpected visibility modifier"
|
| 194 |
+
let attrs β match attrsStx.getOptional? with
|
| 195 |
+
| none => pure #[]
|
| 196 |
+
| some attrs => elabDeclAttrs attrs
|
| 197 |
+
return {
|
| 198 |
+
stx, docString?, visibility, computeKind, recKind, attrs,
|
| 199 |
+
isUnsafe := !unsafeStx.isNone
|
| 200 |
+
}
|
| 201 |
+
|
| 202 |
+
/--
|
| 203 |
+
Ensure the function has not already been declared, and apply the given visibility setting to `declName`.
|
| 204 |
+
If `private`, return the updated name using our internal encoding for private names.
|
| 205 |
+
If `protected`, register `declName` as protected in the environment.
|
| 206 |
+
-/
|
| 207 |
+
def applyVisibility (visibility : Visibility) (declName : Name) : m Name := do
|
| 208 |
+
let mut declName := declName
|
| 209 |
+
if !visibility.isInferredPublic (β getEnv) then
|
| 210 |
+
declName := mkPrivateName (β getEnv) declName
|
| 211 |
+
checkNotAlreadyDeclared declName
|
| 212 |
+
if visibility matches .protected then
|
| 213 |
+
modifyEnv fun env => addProtected env declName
|
| 214 |
+
pure declName
|
| 215 |
+
|
| 216 |
+
def checkIfShadowingStructureField (declName : Name) : m Unit := do
|
| 217 |
+
match declName with
|
| 218 |
+
| Name.str pre .. =>
|
| 219 |
+
if isStructure (β getEnv) pre then
|
| 220 |
+
let fieldNames := getStructureFieldsFlattened (β getEnv) pre
|
| 221 |
+
for fieldName in fieldNames do
|
| 222 |
+
if pre ++ fieldName == declName then
|
| 223 |
+
throwError "invalid declaration name '{declName}', structure '{pre}' has field '{fieldName}'"
|
| 224 |
+
| _ => pure ()
|
| 225 |
+
|
| 226 |
+
def mkDeclName (currNamespace : Name) (modifiers : Modifiers) (shortName : Name) : m (Name Γ Name) := do
|
| 227 |
+
let mut shortName := shortName
|
| 228 |
+
let mut currNamespace := currNamespace
|
| 229 |
+
let view := extractMacroScopes shortName
|
| 230 |
+
let name := view.name
|
| 231 |
+
let isRootName := (`_root_).isPrefixOf name
|
| 232 |
+
if name == `_root_ then
|
| 233 |
+
throwError "invalid declaration name `_root_`, `_root_` is a prefix used to refer to the 'root' namespace"
|
| 234 |
+
let declName := if isRootName then { view with name := name.replacePrefix `_root_ Name.anonymous }.review else currNamespace ++ shortName
|
| 235 |
+
if isRootName then
|
| 236 |
+
let .str p s := name | throwError "invalid declaration name '{name}'"
|
| 237 |
+
shortName := Name.mkSimple s
|
| 238 |
+
currNamespace := p.replacePrefix `_root_ Name.anonymous
|
| 239 |
+
checkIfShadowingStructureField declName
|
| 240 |
+
let declName β applyVisibility modifiers.visibility declName
|
| 241 |
+
match modifiers.visibility with
|
| 242 |
+
| Visibility.protected =>
|
| 243 |
+
match currNamespace with
|
| 244 |
+
| .str _ s => return (declName, Name.mkSimple s ++ shortName)
|
| 245 |
+
| _ =>
|
| 246 |
+
if shortName.isAtomic then
|
| 247 |
+
throwError "protected declarations must be in a namespace"
|
| 248 |
+
return (declName, shortName)
|
| 249 |
+
| _ => return (declName, shortName)
|
| 250 |
+
|
| 251 |
+
/--
|
| 252 |
+
`declId` is of the form
|
| 253 |
+
```
|
| 254 |
+
leading_parser ident >> optional (".{" >> sepBy1 ident ", " >> "}")
|
| 255 |
+
```
|
| 256 |
+
but we also accept a single identifier to users to make macro writing more convenient .
|
| 257 |
+
-/
|
| 258 |
+
def expandDeclIdCore (declId : Syntax) : Name Γ Syntax :=
|
| 259 |
+
if declId.isIdent then
|
| 260 |
+
(declId.getId, mkNullNode)
|
| 261 |
+
else
|
| 262 |
+
let id := declId[0].getId
|
| 263 |
+
let optUnivDeclStx := declId[1]
|
| 264 |
+
(id, optUnivDeclStx)
|
| 265 |
+
|
| 266 |
+
/-- `expandDeclId` resulting type. -/
|
| 267 |
+
structure ExpandDeclIdResult where
|
| 268 |
+
/-- Short name for recursively referring to the declaration. -/
|
| 269 |
+
shortName : Name
|
| 270 |
+
/-- Fully qualified name that will be used to name the declaration in the kernel. -/
|
| 271 |
+
declName : Name
|
| 272 |
+
/-- Universe parameter names provided using the `universe` command and `.{...}` notation. -/
|
| 273 |
+
levelNames : List Name
|
| 274 |
+
|
| 275 |
+
/--
|
| 276 |
+
Given a declaration identifier (e.g., `ident (".{" ident,+ "}")?`) that may contain explicit universe parameters
|
| 277 |
+
- Ensure the new universe parameters do not shadow universe parameters declared using `universe` command.
|
| 278 |
+
- Create the fully qualified named for the declaration using the current namespace, and given `modifiers`
|
| 279 |
+
- Create a short version for recursively referring to the declaration. Recall that the `protected` modifier affects the generation of the short name.
|
| 280 |
+
|
| 281 |
+
The result also contains the universe parameters provided using `universe` command, and the `.{...}` notation.
|
| 282 |
+
|
| 283 |
+
This commands also stores the doc string stored in `modifiers`.
|
| 284 |
+
-/
|
| 285 |
+
def expandDeclId (currNamespace : Name) (currLevelNames : List Name) (declId : Syntax) (modifiers : Modifiers) : m ExpandDeclIdResult := do
|
| 286 |
+
-- ident >> optional (".{" >> sepBy1 ident ", " >> "}")
|
| 287 |
+
let (shortName, optUnivDeclStx) := expandDeclIdCore declId
|
| 288 |
+
let levelNames β if optUnivDeclStx.isNone then
|
| 289 |
+
pure currLevelNames
|
| 290 |
+
else
|
| 291 |
+
let extraLevels := optUnivDeclStx[1].getArgs.getEvenElems
|
| 292 |
+
extraLevels.foldlM
|
| 293 |
+
(fun levelNames idStx =>
|
| 294 |
+
let id := idStx.getId
|
| 295 |
+
if levelNames.elem id then
|
| 296 |
+
withRef idStx <| throwAlreadyDeclaredUniverseLevel id
|
| 297 |
+
else
|
| 298 |
+
pure (id :: levelNames))
|
| 299 |
+
currLevelNames
|
| 300 |
+
let (declName, shortName) β withRef declId <| mkDeclName currNamespace modifiers shortName
|
| 301 |
+
addDocString' declName modifiers.docString?
|
| 302 |
+
return { shortName := shortName, declName := declName, levelNames := levelNames }
|
| 303 |
+
|
| 304 |
+
end Methods
|
| 305 |
+
|
| 306 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclNameGen.lean
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Kyle Miller
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Command
|
| 8 |
+
|
| 9 |
+
/-!
|
| 10 |
+
# Name generator for declarations
|
| 11 |
+
|
| 12 |
+
This module provides functionality to generate a name for a declaration using its binders and its type.
|
| 13 |
+
This is used to generate names for anonymous instances.
|
| 14 |
+
|
| 15 |
+
It uses heuristics to generate an informative but terse name given its namespace, supplied binders, and type.
|
| 16 |
+
It tries to make these relatively unique,
|
| 17 |
+
and it uses suffixes derived from the module to ensure cross-project uniqueness
|
| 18 |
+
when the instance doesn't refer to anything defined in the current project.
|
| 19 |
+
|
| 20 |
+
The name generator can be thought of as a kind of pretty printer, rendering an expression in textual form.
|
| 21 |
+
The general structure of this generator is
|
| 22 |
+
1. `Lean.Elab.Command.NameGen.winnowExpr` takes an expression and re-uses `Expr` as a data structure
|
| 23 |
+
to record the "Syntax"-like structure.
|
| 24 |
+
2. `Lean.Elab.Command.NameGen.mkBaseNameCore` formats the result of that as a string.
|
| 25 |
+
It actually does a bit more computation than that, since it further removes duplicate expressions,
|
| 26 |
+
reporting only the first occurrence of each subexpression.
|
| 27 |
+
-/
|
| 28 |
+
|
| 29 |
+
namespace Lean.Elab.Command
|
| 30 |
+
|
| 31 |
+
open Meta
|
| 32 |
+
|
| 33 |
+
namespace NameGen
|
| 34 |
+
|
| 35 |
+
/--
|
| 36 |
+
If `e` is an application of a projection to a parent structure, returns the term being projected.
|
| 37 |
+
-/
|
| 38 |
+
private def getParentProjArg (e : Expr) : MetaM (Option Expr) := do
|
| 39 |
+
let .const c@(.str _ field) _ := e.getAppFn | return none
|
| 40 |
+
let env β getEnv
|
| 41 |
+
let some info := env.getProjectionFnInfo? c | return none
|
| 42 |
+
unless e.getAppNumArgs == info.numParams + 1 do return none
|
| 43 |
+
let some (.ctorInfo cVal) := env.find? info.ctorName | return none
|
| 44 |
+
if isSubobjectField? env cVal.induct (Name.mkSimple field) |>.isNone then return none
|
| 45 |
+
return e.appArg!
|
| 46 |
+
|
| 47 |
+
/--
|
| 48 |
+
Strips out universes and arguments we decide are unnecessary for naming.
|
| 49 |
+
The resulting expression can have loose fvars and be mangled in other ways.
|
| 50 |
+
Expressions can also be replaced by `.bvar 0` if they shouldn't be mentioned.
|
| 51 |
+
-/
|
| 52 |
+
private partial def winnowExpr (e : Expr) : MetaM Expr := do
|
| 53 |
+
let rec visit (e : Expr) : MonadCacheT Expr Expr MetaM Expr := checkCache e fun _ => do
|
| 54 |
+
if β isProof e then
|
| 55 |
+
return .bvar 0
|
| 56 |
+
match e with
|
| 57 |
+
| .app .. =>
|
| 58 |
+
if let some e' β getParentProjArg e then
|
| 59 |
+
return (β visit e')
|
| 60 |
+
e.withApp fun f args => do
|
| 61 |
+
-- Visit only the explicit arguments to `f` and also its type (and type family) arguments.
|
| 62 |
+
-- The reason we visit type arguments is so that, for example, `Decidable (_ < _)` has
|
| 63 |
+
-- a chance to insert type information. Type families are for reporting things such as type constructors and monads.
|
| 64 |
+
let mut fty β inferType f
|
| 65 |
+
let mut j := 0
|
| 66 |
+
let mut e' β visit f
|
| 67 |
+
for h : i in [0:args.size] do
|
| 68 |
+
unless fty.isForall do
|
| 69 |
+
fty β withTransparency .all <| whnf <| fty.instantiateRevRange j i args
|
| 70 |
+
j := i
|
| 71 |
+
let .forallE _ _ fty' bi := fty | failure
|
| 72 |
+
fty := fty'
|
| 73 |
+
let arg := args[i]
|
| 74 |
+
if β pure bi.isExplicit <||> (pure !arg.isSort <&&> isTypeFormer arg) then
|
| 75 |
+
unless (β isProof arg) do
|
| 76 |
+
e' := .app e' (β visit arg)
|
| 77 |
+
return e'
|
| 78 |
+
| .forallE n ty body bi =>
|
| 79 |
+
withLocalDecl n bi ty fun arg => do
|
| 80 |
+
-- In a dependent forall the body implies `ty`, so we won't mention it.
|
| 81 |
+
let ty' β if body.hasLooseBVars then pure (.bvar 0) else visit ty
|
| 82 |
+
return .forallE n ty' (β visit (body.instantiate1 arg)) bi
|
| 83 |
+
| .lam n ty body bi =>
|
| 84 |
+
if let some e := Expr.etaExpandedStrict? e then
|
| 85 |
+
visit e
|
| 86 |
+
else
|
| 87 |
+
withLocalDecl n bi ty fun arg => do
|
| 88 |
+
-- Don't record the `.lam` since `ty` should be recorded elsewhere in the expression.
|
| 89 |
+
visit (body.instantiate1 arg)
|
| 90 |
+
| .letE _n _t v b _ => visit (b.instantiate1 v)
|
| 91 |
+
| .sort .. =>
|
| 92 |
+
if e.isProp then return .sort levelZero
|
| 93 |
+
else if e.isType then return .sort levelOne
|
| 94 |
+
else return .sort (.param `u)
|
| 95 |
+
| .const name .. => return .const name []
|
| 96 |
+
| .mdata _ e' => visit e'
|
| 97 |
+
| _ => return .bvar 0
|
| 98 |
+
visit e |>.run
|
| 99 |
+
|
| 100 |
+
/--
|
| 101 |
+
State for name generation.
|
| 102 |
+
-/
|
| 103 |
+
private structure MkNameState where
|
| 104 |
+
/-- Keeps track of expressions already visited so that we do not include them again. -/
|
| 105 |
+
seen : ExprSet := {}
|
| 106 |
+
/-- Keeps track of constants that appear in the generated name. -/
|
| 107 |
+
consts : NameSet := {}
|
| 108 |
+
|
| 109 |
+
/--
|
| 110 |
+
Monad for name generation.
|
| 111 |
+
-/
|
| 112 |
+
private abbrev MkNameM := StateRefT MkNameState MetaM
|
| 113 |
+
|
| 114 |
+
/--
|
| 115 |
+
Core algorithm for generating a name. The provided expression should be a winnowed expression.
|
| 116 |
+
|
| 117 |
+
- `omitTopForall` if true causes "Forall" to not be included if the binding type results in an empty string.
|
| 118 |
+
-/
|
| 119 |
+
private def mkBaseNameCore (e : Expr) (omitTopForall : Bool := false) : MkNameM String :=
|
| 120 |
+
visit e omitTopForall
|
| 121 |
+
where
|
| 122 |
+
visit (e : Expr) (omitTopForall : Bool := false) : MkNameM String := do
|
| 123 |
+
if (β get).seen.contains e then
|
| 124 |
+
return ""
|
| 125 |
+
else
|
| 126 |
+
let s β visit' e omitTopForall
|
| 127 |
+
modify fun st => {st with seen := st.seen.insert e}
|
| 128 |
+
return s
|
| 129 |
+
visit' (e : Expr) (omitTopForall : Bool) : MkNameM String := do
|
| 130 |
+
match e with
|
| 131 |
+
| .const name .. =>
|
| 132 |
+
modify (fun st => {st with consts := st.consts.insert name})
|
| 133 |
+
return match name.eraseMacroScopes with
|
| 134 |
+
| .str _ str => str.capitalize
|
| 135 |
+
| _ => ""
|
| 136 |
+
| .app f x => (Β· ++ Β·) <$> visit f <*> visit x
|
| 137 |
+
| .forallE _ ty body _ =>
|
| 138 |
+
let sty β visit ty
|
| 139 |
+
if omitTopForall && sty == "" then
|
| 140 |
+
visit body true
|
| 141 |
+
else
|
| 142 |
+
("Forall" ++ sty ++ Β·) <$> visit body
|
| 143 |
+
| .sort .zero => return "Prop"
|
| 144 |
+
| .sort (.succ _) => return "Type"
|
| 145 |
+
| .sort _ => return "Sort"
|
| 146 |
+
| _ => return ""
|
| 147 |
+
|
| 148 |
+
/--
|
| 149 |
+
Generate a name, while naming the top-level foralls using "Of".
|
| 150 |
+
The provided expression should be a winnowed expression.
|
| 151 |
+
-/
|
| 152 |
+
private partial def mkBaseNameAux (e : Expr) : MkNameM String := do
|
| 153 |
+
let (foralls, sb) β visit e
|
| 154 |
+
return sb ++ String.join foralls
|
| 155 |
+
where
|
| 156 |
+
visit (e : Expr) : MkNameM (List String Γ String) := do
|
| 157 |
+
match e with
|
| 158 |
+
| .forallE _ ty body _ =>
|
| 159 |
+
let (foralls, sb) β visit body
|
| 160 |
+
let st β mkBaseNameCore ty (omitTopForall := true)
|
| 161 |
+
if st == "" then
|
| 162 |
+
return (foralls, sb)
|
| 163 |
+
else
|
| 164 |
+
return (("Of" ++ st) :: foralls, sb)
|
| 165 |
+
| _ => return ([], β mkBaseNameCore e)
|
| 166 |
+
|
| 167 |
+
/--
|
| 168 |
+
Adds all prefixes of `ns` as seen constants.
|
| 169 |
+
-/
|
| 170 |
+
private def visitNamespace (ns : Name) : MkNameM Unit := do
|
| 171 |
+
match ns with
|
| 172 |
+
| .anonymous => pure ()
|
| 173 |
+
| .num ns' _ => visitNamespace ns'
|
| 174 |
+
| .str ns' _ =>
|
| 175 |
+
let env β getEnv
|
| 176 |
+
if env.contains ns then
|
| 177 |
+
modify fun st => {st with seen := st.seen.insert (.const ns []), consts := st.consts.insert ns}
|
| 178 |
+
visitNamespace ns'
|
| 179 |
+
|
| 180 |
+
/--
|
| 181 |
+
Given an expression, generates a "base name" for a declaration.
|
| 182 |
+
The top-level foralls in `e` are treated as being binders, so use the `...Of...` naming convention.
|
| 183 |
+
The current namespace is used to seed the seen expressions with each prefix of the namespace that's a global constant.
|
| 184 |
+
|
| 185 |
+
Collects all constants that contribute to the name in the `MkInstM` state.
|
| 186 |
+
This can be used to decide whether to further transform the generated name;
|
| 187 |
+
in particular, this enables checking whether the generated name mentions declarations
|
| 188 |
+
from the current module or project.
|
| 189 |
+
-/
|
| 190 |
+
def mkBaseName (e : Expr) : MkNameM String := do
|
| 191 |
+
let e β instantiateMVars e
|
| 192 |
+
visitNamespace (β getCurrNamespace)
|
| 193 |
+
mkBaseNameAux (β winnowExpr e)
|
| 194 |
+
|
| 195 |
+
/--
|
| 196 |
+
Converts a module name into a suffix. Includes a leading `_`,
|
| 197 |
+
so for example `Lean.Elab.DefView` becomes `_lean_elab_defView`.
|
| 198 |
+
-/
|
| 199 |
+
private def moduleToSuffix : Name β String
|
| 200 |
+
| .anonymous => ""
|
| 201 |
+
| .num n _ => moduleToSuffix n
|
| 202 |
+
| .str n s => moduleToSuffix n ++ "_" ++ s.decapitalize
|
| 203 |
+
|
| 204 |
+
/--
|
| 205 |
+
Uses heuristics to generate an informative but terse base name for a term of the given type, using `mkBaseName`.
|
| 206 |
+
Makes use of the current namespace.
|
| 207 |
+
It tries to make these names relatively unique ecosystem-wide,
|
| 208 |
+
and it adds suffixes using the current module if the resulting name doesn't refer to anything defined in this module.
|
| 209 |
+
|
| 210 |
+
If any constant in `type` has a name with macro scopes, then the result will be a name with fresh macro scopes.
|
| 211 |
+
While in this case we could skip the naming heuristics, we still want informative names for debugging purposes.
|
| 212 |
+
-/
|
| 213 |
+
def mkBaseNameWithSuffix (pre : String) (type : Expr) : MetaM Name := do
|
| 214 |
+
let (name, st) β mkBaseName type |>.run {}
|
| 215 |
+
let name := pre ++ name
|
| 216 |
+
let project := (β getMainModule).getRoot
|
| 217 |
+
-- Collect the modules for each constant that appeared.
|
| 218 |
+
let modules β st.consts.foldM (init := Array.mkEmpty st.consts.size) fun mods name => return mods.push (β findModuleOf? name)
|
| 219 |
+
-- We can avoid adding the suffix if the instance refers to module-local names.
|
| 220 |
+
let isModuleLocal := modules.any Option.isNone
|
| 221 |
+
-- We can also avoid adding the full module suffix if the instance refers to "project"-local names.
|
| 222 |
+
let isProjectLocal := isModuleLocal || modules.any fun mod? => mod?.map (Β·.getRoot) == project
|
| 223 |
+
let name := Name.mkSimple <|
|
| 224 |
+
if !isProjectLocal then
|
| 225 |
+
s!"{name}{moduleToSuffix project}"
|
| 226 |
+
else
|
| 227 |
+
name
|
| 228 |
+
if Option.isSome <| type.find? (fun e => if let .const n _ := e then n.hasMacroScopes else false) then
|
| 229 |
+
mkFreshUserName name
|
| 230 |
+
else
|
| 231 |
+
return name
|
| 232 |
+
|
| 233 |
+
/--
|
| 234 |
+
Elaborates the binders and type and then uses `mkBaseNameWithSuffix` to generate a name.
|
| 235 |
+
Furthermore, uses `mkUnusedBaseName` on the result.
|
| 236 |
+
-/
|
| 237 |
+
def mkBaseNameWithSuffix' (pre : String) (binders : Array Syntax) (type : Syntax) : TermElabM Name := do
|
| 238 |
+
let name β
|
| 239 |
+
try
|
| 240 |
+
Term.withAutoBoundImplicit <| Term.elabBinders binders fun binds => Term.withoutErrToSorry do
|
| 241 |
+
let ty β mkForallFVars binds (β Term.elabType type)
|
| 242 |
+
mkBaseNameWithSuffix pre ty
|
| 243 |
+
catch _ =>
|
| 244 |
+
mkFreshUserName <| Name.mkSimple pre
|
| 245 |
+
liftMacroM <| mkUnusedBaseName name
|
| 246 |
+
|
| 247 |
+
end NameGen
|
| 248 |
+
|
| 249 |
+
/--
|
| 250 |
+
Generates an instance name for a declaration that has the given binders and type.
|
| 251 |
+
It tries to make these names relatively unique ecosystem-wide.
|
| 252 |
+
|
| 253 |
+
Note that this elaborates the binders and the type.
|
| 254 |
+
This means that when elaborating an instance declaration, we elaborate these twice.
|
| 255 |
+
-/
|
| 256 |
+
def mkInstanceName (binders : Array Syntax) (type : Syntax) : CommandElabM Name := do
|
| 257 |
+
let savedState β get
|
| 258 |
+
try
|
| 259 |
+
-- Unfortunately we can't include any of the binders from `runTermElabM` since, without
|
| 260 |
+
-- elaborating the body of the instance, we have no idea which of these binders are
|
| 261 |
+
-- actually used.
|
| 262 |
+
runTermElabM fun _ => NameGen.mkBaseNameWithSuffix' "inst" binders type
|
| 263 |
+
finally
|
| 264 |
+
set savedState
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclUtil.lean
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Basic
|
| 8 |
+
import Lean.Meta.Check
|
| 9 |
+
|
| 10 |
+
namespace Lean.Meta
|
| 11 |
+
|
| 12 |
+
def forallTelescopeCompatibleAux (k : Array Expr β Expr β Expr β MetaM Ξ±) : Nat β Expr β Expr β Array Expr β MetaM Ξ±
|
| 13 |
+
| 0, typeβ, typeβ, xs => k xs typeβ typeβ
|
| 14 |
+
| i+1, typeβ, typeβ, xs => do
|
| 15 |
+
let typeβ β whnf typeβ
|
| 16 |
+
let typeβ β whnf typeβ
|
| 17 |
+
match typeβ, typeβ with
|
| 18 |
+
| .forallE nβ dβ bβ cβ, .forallE nβ dβ bβ cβ =>
|
| 19 |
+
-- Remark: we use `mkIdent` to ensure macroscopes do not leak into error messages
|
| 20 |
+
unless cβ == cβ do
|
| 21 |
+
throwError "binder annotation mismatch at parameter '{mkIdent nβ}'"
|
| 22 |
+
/-
|
| 23 |
+
Remark: recall that users may suppress parameter names for instance implicit arguments.
|
| 24 |
+
A fresh name (with macro scopes) is generated in this case. Thus, we allow the names
|
| 25 |
+
to be different in this case. See issue #4310.
|
| 26 |
+
-/
|
| 27 |
+
unless nβ == nβ || (cβ.isInstImplicit && nβ.hasMacroScopes && nβ.hasMacroScopes) do
|
| 28 |
+
throwError "parameter name mismatch '{mkIdent nβ}', expected '{mkIdent nβ}'"
|
| 29 |
+
unless (β isDefEq dβ dβ) do
|
| 30 |
+
throwError "parameter '{mkIdent nβ}' {β mkHasTypeButIsExpectedMsg dβ dβ}"
|
| 31 |
+
withLocalDecl nβ cβ dβ fun x =>
|
| 32 |
+
let typeβ := bβ.instantiate1 x
|
| 33 |
+
let typeβ := bβ.instantiate1 x
|
| 34 |
+
forallTelescopeCompatibleAux k i typeβ typeβ (xs.push x)
|
| 35 |
+
| _, _ => throwError "unexpected number of parameters"
|
| 36 |
+
|
| 37 |
+
/-- Given two forall-expressions `typeβ` and `typeβ`, ensure the first `numParams` parameters are compatible, and
|
| 38 |
+
then execute `k` with the parameters and remaining types. -/
|
| 39 |
+
def forallTelescopeCompatible [Monad m] [MonadControlT MetaM m] (typeβ typeβ : Expr) (numParams : Nat) (k : Array Expr β Expr β Expr β m Ξ±) : m Ξ± :=
|
| 40 |
+
controlAt MetaM fun runInBase =>
|
| 41 |
+
forallTelescopeCompatibleAux (fun xs typeβ typeβ => runInBase $ k xs typeβ typeβ) numParams typeβ typeβ #[]
|
| 42 |
+
|
| 43 |
+
end Meta
|
| 44 |
+
|
| 45 |
+
namespace Elab
|
| 46 |
+
|
| 47 |
+
def expandOptDeclSig (stx : Syntax) : Syntax Γ Option Syntax :=
|
| 48 |
+
-- many Term.bracketedBinder >> Term.optType
|
| 49 |
+
let binders := stx[0]
|
| 50 |
+
let optType := stx[1] -- optional (leading_parser " : " >> termParser)
|
| 51 |
+
if optType.isNone then
|
| 52 |
+
(binders, none)
|
| 53 |
+
else
|
| 54 |
+
let typeSpec := optType[0]
|
| 55 |
+
(binders, some typeSpec[1])
|
| 56 |
+
|
| 57 |
+
def expandDeclSig (stx : Syntax) : Syntax Γ Syntax :=
|
| 58 |
+
-- many Term.bracketedBinder >> Term.typeSpec
|
| 59 |
+
let binders := stx[0]
|
| 60 |
+
let typeSpec := stx[1]
|
| 61 |
+
(binders, typeSpec[1])
|
| 62 |
+
|
| 63 |
+
/--
|
| 64 |
+
Sort the given list of `usedParams` using the following order:
|
| 65 |
+
- If it is an explicit level in `allUserParams`, then use user-given order.
|
| 66 |
+
- All other levels come in lexicographic order after these.
|
| 67 |
+
|
| 68 |
+
Remark: `scopeParams` are the universe params introduced using the `universe` command. `allUserParams` contains
|
| 69 |
+
the universe params introduced using the `universe` command *and* the `.{...}` notation.
|
| 70 |
+
|
| 71 |
+
Remark: this function return an exception if there is an `u` not in `usedParams`, that is in `allUserParams` but not in `scopeParams`.
|
| 72 |
+
|
| 73 |
+
Remark: `scopeParams` and `allUserParams` are in reverse declaration order. That is, the head is the last declared parameter.
|
| 74 |
+
-/
|
| 75 |
+
def sortDeclLevelParams (scopeParams : List Name) (allUserParams : List Name) (usedParams : Array Name) : Except String (List Name) :=
|
| 76 |
+
match allUserParams.find? fun u => !usedParams.contains u && !scopeParams.elem u with
|
| 77 |
+
| some u => throw s!"unused universe parameter '{u}'"
|
| 78 |
+
| none =>
|
| 79 |
+
-- Recall that `allUserParams` (like `scopeParams`) are in reverse order. That is, the last declared universe is the first element of the list.
|
| 80 |
+
-- The following `foldl` will reverse the elements and produce a list of universe levels using the user given order.
|
| 81 |
+
let result := allUserParams.foldl (fun result levelName => if usedParams.elem levelName then levelName :: result else result) []
|
| 82 |
+
let remaining := usedParams.filter (fun levelParam => !allUserParams.elem levelParam)
|
| 83 |
+
let remaining := remaining.qsort Name.lt
|
| 84 |
+
return result ++ remaining.toList
|
| 85 |
+
|
| 86 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Declaration.lean
ADDED
|
@@ -0,0 +1,347 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Util.CollectLevelParams
|
| 8 |
+
import Lean.Elab.DeclUtil
|
| 9 |
+
import Lean.Elab.DefView
|
| 10 |
+
import Lean.Elab.MutualDef
|
| 11 |
+
import Lean.Elab.MutualInductive
|
| 12 |
+
import Lean.Elab.DeclarationRange
|
| 13 |
+
namespace Lean.Elab.Command
|
| 14 |
+
|
| 15 |
+
open Meta
|
| 16 |
+
|
| 17 |
+
private def ensureValidNamespace (name : Name) : MacroM Unit := do
|
| 18 |
+
match name with
|
| 19 |
+
| .str p s =>
|
| 20 |
+
if s == "_root_" then
|
| 21 |
+
Macro.throwError s!"invalid namespace '{name}', '_root_' is a reserved namespace"
|
| 22 |
+
ensureValidNamespace p
|
| 23 |
+
| .num .. => Macro.throwError s!"invalid namespace '{name}', it must not contain numeric parts"
|
| 24 |
+
| .anonymous => return ()
|
| 25 |
+
|
| 26 |
+
private def setDeclIdName (declId : Syntax) (nameNew : Name) : Syntax :=
|
| 27 |
+
let (id, _) := expandDeclIdCore declId
|
| 28 |
+
-- We should not update the name of `def _root_.` declarations
|
| 29 |
+
assert! !(`_root_).isPrefixOf id
|
| 30 |
+
let idStx := mkIdent nameNew |>.raw.setInfo declId.getHeadInfo
|
| 31 |
+
if declId.isIdent then
|
| 32 |
+
idStx
|
| 33 |
+
else
|
| 34 |
+
declId.setArg 0 idStx
|
| 35 |
+
|
| 36 |
+
/-- Return `true` if `stx` is a `Command.declaration`, and it is a definition that always has a name. -/
|
| 37 |
+
private def isNamedDef (stx : Syntax) : Bool :=
|
| 38 |
+
if !stx.isOfKind ``Lean.Parser.Command.declaration then
|
| 39 |
+
false
|
| 40 |
+
else
|
| 41 |
+
let decl := stx[1]
|
| 42 |
+
let k := decl.getKind
|
| 43 |
+
k == ``Lean.Parser.Command.abbrev ||
|
| 44 |
+
k == ``Lean.Parser.Command.definition ||
|
| 45 |
+
k == ``Lean.Parser.Command.theorem ||
|
| 46 |
+
k == ``Lean.Parser.Command.opaque ||
|
| 47 |
+
k == ``Lean.Parser.Command.axiom ||
|
| 48 |
+
k == ``Lean.Parser.Command.inductive ||
|
| 49 |
+
k == ``Lean.Parser.Command.classInductive ||
|
| 50 |
+
k == ``Lean.Parser.Command.structure
|
| 51 |
+
|
| 52 |
+
/-- Return `true` if `stx` is an `instance` declaration command -/
|
| 53 |
+
private def isInstanceDef (stx : Syntax) : Bool :=
|
| 54 |
+
stx.isOfKind ``Lean.Parser.Command.declaration &&
|
| 55 |
+
stx[1].getKind == ``Lean.Parser.Command.instance
|
| 56 |
+
|
| 57 |
+
/-- Return `some name` if `stx` is a definition named `name` -/
|
| 58 |
+
private def getDefName? (stx : Syntax) : Option Name := do
|
| 59 |
+
if isNamedDef stx then
|
| 60 |
+
let (id, _) := expandDeclIdCore stx[1][1]
|
| 61 |
+
some id
|
| 62 |
+
else if isInstanceDef stx then
|
| 63 |
+
let optDeclId := stx[1][3]
|
| 64 |
+
if optDeclId.isNone then none
|
| 65 |
+
else
|
| 66 |
+
let (id, _) := expandDeclIdCore optDeclId[0]
|
| 67 |
+
some id
|
| 68 |
+
else
|
| 69 |
+
none
|
| 70 |
+
|
| 71 |
+
/--
|
| 72 |
+
Update the name of the given definition.
|
| 73 |
+
This function assumes `stx` is not a nameless instance.
|
| 74 |
+
-/
|
| 75 |
+
private def setDefName (stx : Syntax) (name : Name) : Syntax :=
|
| 76 |
+
if isNamedDef stx then
|
| 77 |
+
stx.setArg 1 <| stx[1].setArg 1 <| setDeclIdName stx[1][1] name
|
| 78 |
+
else if isInstanceDef stx then
|
| 79 |
+
-- We never set the name of nameless instance declarations
|
| 80 |
+
assert! !stx[1][3].isNone
|
| 81 |
+
stx.setArg 1 <| stx[1].setArg 3 <| stx[1][3].setArg 0 <| setDeclIdName stx[1][3][0] name
|
| 82 |
+
else
|
| 83 |
+
stx
|
| 84 |
+
|
| 85 |
+
/--
|
| 86 |
+
Given declarations such as `@[...] def Foo.Bla.f ...` return `some (Foo.Bla, @[...] def f ...)`
|
| 87 |
+
Remark: if the id starts with `_root_`, we return `none`.
|
| 88 |
+
-/
|
| 89 |
+
private def expandDeclNamespace? (stx : Syntax) : MacroM (Option (Name Γ Syntax)) := do
|
| 90 |
+
let some name := getDefName? stx | return none
|
| 91 |
+
if (`_root_).isPrefixOf name then
|
| 92 |
+
ensureValidNamespace (name.replacePrefix `_root_ Name.anonymous)
|
| 93 |
+
return none
|
| 94 |
+
let scpView := extractMacroScopes name
|
| 95 |
+
match scpView.name with
|
| 96 |
+
| .str .anonymous _ => return none
|
| 97 |
+
| .str pre shortName => return some (pre, setDefName stx { scpView with name := .mkSimple shortName }.review)
|
| 98 |
+
| _ => return none
|
| 99 |
+
|
| 100 |
+
def elabAxiom (modifiers : Modifiers) (stx : Syntax) : CommandElabM Unit := do
|
| 101 |
+
-- leading_parser "axiom " >> declId >> declSig
|
| 102 |
+
let declId := stx[1]
|
| 103 |
+
let (binders, typeStx) := expandDeclSig stx[2]
|
| 104 |
+
runTermElabM fun vars => do
|
| 105 |
+
let scopeLevelNames β Term.getLevelNames
|
| 106 |
+
let β¨shortName, declName, allUserLevelNamesβ© β Term.expandDeclId (β getCurrNamespace) scopeLevelNames declId modifiers
|
| 107 |
+
addDeclarationRangesForBuiltin declName modifiers.stx stx
|
| 108 |
+
Term.withAutoBoundImplicit do
|
| 109 |
+
Term.withAutoBoundImplicitForbiddenPred (fun n => shortName == n) do
|
| 110 |
+
Term.withDeclName declName <| Term.withLevelNames allUserLevelNames <| Term.elabBinders binders.getArgs fun xs => do
|
| 111 |
+
Term.applyAttributesAt declName modifiers.attrs AttributeApplicationTime.beforeElaboration
|
| 112 |
+
let type β Term.elabType typeStx
|
| 113 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 114 |
+
let xs β Term.addAutoBoundImplicits xs (declId.getTailPos? (canonicalOnly := true))
|
| 115 |
+
let type β instantiateMVars type
|
| 116 |
+
let type β mkForallFVars xs type
|
| 117 |
+
let type β mkForallFVars vars type (usedOnly := true)
|
| 118 |
+
let type β Term.levelMVarToParam type
|
| 119 |
+
let usedParams := collectLevelParams {} type |>.params
|
| 120 |
+
match sortDeclLevelParams scopeLevelNames allUserLevelNames usedParams with
|
| 121 |
+
| Except.error msg => throwErrorAt stx msg
|
| 122 |
+
| Except.ok levelParams =>
|
| 123 |
+
let type β instantiateMVars type
|
| 124 |
+
let decl := Declaration.axiomDecl {
|
| 125 |
+
name := declName,
|
| 126 |
+
levelParams := levelParams,
|
| 127 |
+
type := type,
|
| 128 |
+
isUnsafe := modifiers.isUnsafe
|
| 129 |
+
}
|
| 130 |
+
trace[Elab.axiom] "{declName} : {type}"
|
| 131 |
+
Term.ensureNoUnassignedMVars decl
|
| 132 |
+
addDecl decl
|
| 133 |
+
withSaveInfoContext do -- save new env
|
| 134 |
+
Term.addTermInfo' declId (β mkConstWithLevelParams declName) (isBinder := true)
|
| 135 |
+
Term.applyAttributesAt declName modifiers.attrs AttributeApplicationTime.afterTypeChecking
|
| 136 |
+
if isExtern (β getEnv) declName then
|
| 137 |
+
compileDecl decl
|
| 138 |
+
Term.applyAttributesAt declName modifiers.attrs AttributeApplicationTime.afterCompilation
|
| 139 |
+
|
| 140 |
+
/--
|
| 141 |
+
Macro that expands a declaration with a complex name into an explicit `namespace` block.
|
| 142 |
+
Implementing this step as a macro means that reuse checking is handled by `elabCommand`.
|
| 143 |
+
-/
|
| 144 |
+
@[builtin_macro Lean.Parser.Command.declaration]
|
| 145 |
+
def expandNamespacedDeclaration : Macro := fun stx => do
|
| 146 |
+
match (β expandDeclNamespace? stx) with
|
| 147 |
+
| some (ns, newStx) => do
|
| 148 |
+
-- Limit ref variability for incrementality; see Note [Incremental Macros]
|
| 149 |
+
let declTk := stx[1][0]
|
| 150 |
+
let ns := mkIdentFrom declTk ns
|
| 151 |
+
withRef declTk `(namespace $ns $(β¨newStxβ©) end $ns)
|
| 152 |
+
| none => Macro.throwUnsupported
|
| 153 |
+
|
| 154 |
+
@[builtin_command_elab declaration, builtin_incremental]
|
| 155 |
+
def elabDeclaration : CommandElab := fun stx => do
|
| 156 |
+
withExporting (isExporting := (β getScope).isPublic) do
|
| 157 |
+
let modifiers : TSyntax ``Parser.Command.declModifiers := β¨stx[0]β©
|
| 158 |
+
let decl := stx[1]
|
| 159 |
+
let declKind := decl.getKind
|
| 160 |
+
if isDefLike decl then
|
| 161 |
+
-- only case implementing incrementality currently
|
| 162 |
+
elabMutualDef #[stx]
|
| 163 |
+
else withoutCommandIncrementality true do
|
| 164 |
+
let modifiers β elabModifiers modifiers
|
| 165 |
+
withExporting (isExporting := modifiers.isInferredPublic (β getEnv)) do
|
| 166 |
+
if declKind == ``Lean.Parser.Command.Β«axiomΒ» then
|
| 167 |
+
elabAxiom modifiers decl
|
| 168 |
+
else if declKind == ``Lean.Parser.Command.Β«inductiveΒ»
|
| 169 |
+
|| declKind == ``Lean.Parser.Command.classInductive
|
| 170 |
+
|| declKind == ``Lean.Parser.Command.Β«structureΒ» then
|
| 171 |
+
elabInductive modifiers decl
|
| 172 |
+
else
|
| 173 |
+
throwError "unexpected declaration"
|
| 174 |
+
|
| 175 |
+
/-- Return true if all elements of the mutual-block are definitions/theorems/abbrevs. -/
|
| 176 |
+
private def isMutualDef (stx : Syntax) : Bool :=
|
| 177 |
+
stx[1].getArgs.all fun elem =>
|
| 178 |
+
let decl := elem[1]
|
| 179 |
+
isDefLike decl
|
| 180 |
+
|
| 181 |
+
private def isMutualPreambleCommand (stx : Syntax) : Bool :=
|
| 182 |
+
let k := stx.getKind
|
| 183 |
+
k == ``Lean.Parser.Command.variable ||
|
| 184 |
+
k == ``Lean.Parser.Command.universe ||
|
| 185 |
+
k == ``Lean.Parser.Command.check ||
|
| 186 |
+
k == ``Lean.Parser.Command.set_option ||
|
| 187 |
+
k == ``Lean.Parser.Command.open
|
| 188 |
+
|
| 189 |
+
private partial def splitMutualPreamble (elems : Array Syntax) : Option (Array Syntax Γ Array Syntax) :=
|
| 190 |
+
let rec loop (i : Nat) : Option (Array Syntax Γ Array Syntax) :=
|
| 191 |
+
if h : i < elems.size then
|
| 192 |
+
if isMutualPreambleCommand elems[i] then
|
| 193 |
+
loop (i+1)
|
| 194 |
+
else if i == 0 then
|
| 195 |
+
none -- `mutual` block does not contain any preamble commands
|
| 196 |
+
else
|
| 197 |
+
some (elems[*...i], elems[i...elems.size])
|
| 198 |
+
else
|
| 199 |
+
none -- a `mutual` block containing only preamble commands is not a valid `mutual` block
|
| 200 |
+
loop 0
|
| 201 |
+
|
| 202 |
+
/--
|
| 203 |
+
Find the common namespace for the given names.
|
| 204 |
+
Example:
|
| 205 |
+
```
|
| 206 |
+
findCommonPrefix [`Lean.Elab.eval, `Lean.mkConst, `Lean.Elab.Tactic.evalTactic]
|
| 207 |
+
-- `Lean
|
| 208 |
+
```
|
| 209 |
+
-/
|
| 210 |
+
def findCommonPrefix (ns : List Name) : Name :=
|
| 211 |
+
match ns with
|
| 212 |
+
| [] => .anonymous
|
| 213 |
+
| n :: ns => go n ns
|
| 214 |
+
where
|
| 215 |
+
go (n : Name) (ns : List Name) : Name :=
|
| 216 |
+
match n with
|
| 217 |
+
| .anonymous => .anonymous
|
| 218 |
+
| _ => match ns with
|
| 219 |
+
| [] => n
|
| 220 |
+
| n' :: ns => go (findCommon n.components n'.components) ns
|
| 221 |
+
findCommon (as bs : List Name) : Name :=
|
| 222 |
+
match as, bs with
|
| 223 |
+
| a :: as, b :: bs => if a == b then a ++ findCommon as bs else .anonymous
|
| 224 |
+
| _, _ => .anonymous
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
@[builtin_macro Lean.Parser.Command.mutual]
|
| 228 |
+
def expandMutualNamespace : Macro := fun stx => do
|
| 229 |
+
let mut nss := #[]
|
| 230 |
+
for elem in stx[1].getArgs do
|
| 231 |
+
match (β expandDeclNamespace? elem) with
|
| 232 |
+
| none => Macro.throwUnsupported
|
| 233 |
+
| some (n, _) => nss := nss.push n
|
| 234 |
+
let common := findCommonPrefix nss.toList
|
| 235 |
+
if common.isAnonymous then Macro.throwUnsupported
|
| 236 |
+
let elemsNew β stx[1].getArgs.mapM fun elem => do
|
| 237 |
+
let some name := getDefName? elem | unreachable!
|
| 238 |
+
let view := extractMacroScopes name
|
| 239 |
+
let nameNew := { view with name := view.name.replacePrefix common .anonymous }.review
|
| 240 |
+
return setDefName elem nameNew
|
| 241 |
+
let ns := mkIdentFrom stx common
|
| 242 |
+
let stxNew := stx.setArg 1 (mkNullNode elemsNew)
|
| 243 |
+
`(namespace $ns $(β¨stxNewβ©) end $ns)
|
| 244 |
+
|
| 245 |
+
@[builtin_macro Lean.Parser.Command.mutual]
|
| 246 |
+
def expandMutualElement : Macro := fun stx => do
|
| 247 |
+
let mut elemsNew := #[]
|
| 248 |
+
let mut modified := false
|
| 249 |
+
for elem in stx[1].getArgs do
|
| 250 |
+
-- Don't trigger the `expandNamespacedDecl` macro, the namespace is handled by the mutual def
|
| 251 |
+
-- elaborator directly instead
|
| 252 |
+
if elem.isOfKind ``Parser.Command.declaration then
|
| 253 |
+
continue
|
| 254 |
+
match (β expandMacro? elem) with
|
| 255 |
+
| some elemNew => elemsNew := elemsNew.push elemNew; modified := true
|
| 256 |
+
| none => elemsNew := elemsNew.push elem
|
| 257 |
+
if modified then
|
| 258 |
+
return stx.setArg 1 (mkNullNode elemsNew)
|
| 259 |
+
else
|
| 260 |
+
Macro.throwUnsupported
|
| 261 |
+
|
| 262 |
+
@[builtin_macro Lean.Parser.Command.mutual]
|
| 263 |
+
def expandMutualPreamble : Macro := fun stx =>
|
| 264 |
+
match splitMutualPreamble stx[1].getArgs with
|
| 265 |
+
| none => Macro.throwUnsupported
|
| 266 |
+
| some (preamble, rest) => do
|
| 267 |
+
let secCmd β `(section)
|
| 268 |
+
let newMutual := stx.setArg 1 (mkNullNode rest)
|
| 269 |
+
let endCmd β `(end)
|
| 270 |
+
return mkNullNode (#[secCmd] ++ preamble ++ #[newMutual] ++ #[endCmd])
|
| 271 |
+
|
| 272 |
+
@[builtin_command_elab Β«mutualΒ», builtin_incremental]
|
| 273 |
+
def elabMutual : CommandElab := fun stx => do
|
| 274 |
+
withExporting (isExporting := (β getScope).isPublic) do
|
| 275 |
+
if isMutualDef stx then
|
| 276 |
+
-- only case implementing incrementality currently
|
| 277 |
+
elabMutualDef stx[1].getArgs
|
| 278 |
+
else withoutCommandIncrementality true do
|
| 279 |
+
if β isMutualInductive stx then
|
| 280 |
+
elabMutualInductive stx[1].getArgs
|
| 281 |
+
else
|
| 282 |
+
throwError "invalid mutual block: either all elements of the block must be inductive/structure declarations, or they must all be definitions/theorems/abbrevs"
|
| 283 |
+
|
| 284 |
+
/- leading_parser "attribute " >> "[" >> sepBy1 (eraseAttr <|> Term.attrInstance) ", " >> "]" >> many1 ident -/
|
| 285 |
+
@[builtin_command_elab Β«attributeΒ»] def elabAttr : CommandElab := fun stx => do
|
| 286 |
+
let mut attrInsts := #[]
|
| 287 |
+
let mut toErase := #[]
|
| 288 |
+
for attrKindStx in stx[2].getSepArgs do
|
| 289 |
+
if attrKindStx.getKind == ``Lean.Parser.Command.eraseAttr then
|
| 290 |
+
let attrName := attrKindStx[1].getId.eraseMacroScopes
|
| 291 |
+
if isAttribute (β getEnv) attrName then
|
| 292 |
+
toErase := toErase.push attrName
|
| 293 |
+
else
|
| 294 |
+
logErrorAt attrKindStx m!"unknown attribute [{attrName}]"
|
| 295 |
+
else
|
| 296 |
+
attrInsts := attrInsts.push attrKindStx
|
| 297 |
+
let attrs β elabAttrs attrInsts
|
| 298 |
+
let idents := stx[4].getArgs
|
| 299 |
+
for ident in idents do withRef ident <| liftTermElabM do
|
| 300 |
+
/-
|
| 301 |
+
HACK to allow `attribute` command to disable builtin simprocs.
|
| 302 |
+
TODO: find a better solution. Example: have some "fake" declaration
|
| 303 |
+
for builtin simprocs.
|
| 304 |
+
-/
|
| 305 |
+
let declNames β
|
| 306 |
+
try
|
| 307 |
+
realizeGlobalConstWithInfos ident
|
| 308 |
+
catch _ =>
|
| 309 |
+
let name := ident.getId.eraseMacroScopes
|
| 310 |
+
if (β Simp.isBuiltinSimproc name) then
|
| 311 |
+
pure [name]
|
| 312 |
+
else
|
| 313 |
+
throwUnknownConstantAt ident name
|
| 314 |
+
let declName β ensureNonAmbiguous ident declNames
|
| 315 |
+
Term.applyAttributes declName attrs
|
| 316 |
+
for attrName in toErase do
|
| 317 |
+
Attribute.erase declName attrName
|
| 318 |
+
|
| 319 |
+
@[builtin_command_elab Lean.Parser.Command.Β«initializeΒ»] def elabInitialize : CommandElab
|
| 320 |
+
| stx@`($declModifiers:declModifiers $kw:initializeKeyword $[$id? : $type? β]? $doSeq) => do
|
| 321 |
+
let attrId := mkIdentFrom stx <| if kw.raw[0].isToken "initialize" then `init else `builtin_init
|
| 322 |
+
if let (some id, some type) := (id?, type?) then
|
| 323 |
+
let `(Parser.Command.declModifiersT| $[$doc?:docComment]? $[@[$attrs?,*]]? $(vis?)? $[unsafe%$unsafe?]?) := stx[0]
|
| 324 |
+
| throwErrorAt declModifiers "invalid initialization command, unexpected modifiers"
|
| 325 |
+
let defStx β `($[$doc?:docComment]? @[$attrId:ident initFn, $(attrs?.getD β
),*] $(vis?)? opaque $id : $type)
|
| 326 |
+
let mut fullId := (β getCurrNamespace) ++ id.getId
|
| 327 |
+
if vis?.any (Β·.raw.isOfKind ``Parser.Command.private) then
|
| 328 |
+
fullId := mkPrivateName (β getEnv) fullId
|
| 329 |
+
-- We need to add `id`'s ranges *before* elaborating `initFn` (and then `id` itself) as
|
| 330 |
+
-- otherwise the info context created by `with_decl_name` will be incomplete and break the
|
| 331 |
+
-- call hierarchy
|
| 332 |
+
addDeclarationRangesForBuiltin fullId β¨defStx.raw[0]β© defStx.raw[1]
|
| 333 |
+
elabCommand (β `(
|
| 334 |
+
$[unsafe%$unsafe?]? def initFn : IO $type := with_decl_name% $(mkIdent fullId) do $doSeq
|
| 335 |
+
$defStx:command))
|
| 336 |
+
else
|
| 337 |
+
let `(Parser.Command.declModifiersT| $[$doc?:docComment]? $[@[$attrs?,*]]? $(_)? $[unsafe%$unsafe?]?) := declModifiers
|
| 338 |
+
| throwErrorAt declModifiers "invalid initialization command, unexpected modifiers"
|
| 339 |
+
let attrs := (attrs?.map (Β·.getElems)).getD #[]
|
| 340 |
+
let attrs := attrs.push (β `(Lean.Parser.Term.attrInstance| $attrId:ident))
|
| 341 |
+
elabCommand (β `($[$doc?:docComment]? @[$[$attrs],*] $[unsafe%$unsafe?]? def initFn : IO Unit := do $doSeq))
|
| 342 |
+
| _ => throwUnsupportedSyntax
|
| 343 |
+
|
| 344 |
+
builtin_initialize
|
| 345 |
+
registerTraceClass `Elab.axiom
|
| 346 |
+
|
| 347 |
+
end Lean.Elab.Command
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DeclarationRange.lean
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Log
|
| 8 |
+
import Lean.Parser.Command
|
| 9 |
+
import Lean.DeclarationRange
|
| 10 |
+
import Lean.Data.Lsp.Utf16
|
| 11 |
+
|
| 12 |
+
namespace Lean.Elab
|
| 13 |
+
|
| 14 |
+
def getDeclarationRange? [Monad m] [MonadFileMap m] (stx : Syntax) : m (Option DeclarationRange) := do
|
| 15 |
+
let some range := stx.getRange?
|
| 16 |
+
| return none
|
| 17 |
+
let fileMap β getFileMap
|
| 18 |
+
return some <| .ofStringPositions fileMap range.start range.stop
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
For most builtin declarations, the selection range is just its name, which is stored in the second position.
|
| 22 |
+
Example:
|
| 23 |
+
```
|
| 24 |
+
"def " >> declId >> optDeclSig >> declVal
|
| 25 |
+
```
|
| 26 |
+
If the declaration name is absent, we use the keyword instead.
|
| 27 |
+
This function converts the given `Syntax` into one that represents its "selection range".
|
| 28 |
+
-/
|
| 29 |
+
def getDeclarationSelectionRef (stx : Syntax) : Syntax :=
|
| 30 |
+
if stx.isOfKind ``Lean.Parser.Command.instance then
|
| 31 |
+
-- must skip `attrKind` and `optPrio` for `instance`
|
| 32 |
+
if !stx[3].isNone then
|
| 33 |
+
stx[3][0]
|
| 34 |
+
else
|
| 35 |
+
stx[1]
|
| 36 |
+
else
|
| 37 |
+
if stx[1][0].isIdent then
|
| 38 |
+
stx[1][0] -- `declId`
|
| 39 |
+
else if stx[1].isIdent then
|
| 40 |
+
stx[1] -- raw `ident`
|
| 41 |
+
else
|
| 42 |
+
stx[0]
|
| 43 |
+
|
| 44 |
+
/--
|
| 45 |
+
Derives and adds declaration ranges from given syntax trees. If `rangeStx` does not have a range,
|
| 46 |
+
nothing is added. If `selectionRangeStx` does not have a range, it is defaulted to that of
|
| 47 |
+
`rangeStx`.
|
| 48 |
+
-/
|
| 49 |
+
def addDeclarationRangesFromSyntax [Monad m] [MonadEnv m] [MonadFileMap m] (declName : Name)
|
| 50 |
+
(rangeStx : Syntax) (selectionRangeStx : Syntax := .missing) : m Unit := do
|
| 51 |
+
-- may fail on partial syntax, ignore in that case
|
| 52 |
+
let some range β getDeclarationRange? rangeStx | return
|
| 53 |
+
let selectionRange β (Β·.getD range) <$> getDeclarationRange? selectionRangeStx
|
| 54 |
+
Lean.addDeclarationRanges declName { range, selectionRange }
|
| 55 |
+
|
| 56 |
+
/--
|
| 57 |
+
Stores the `range` and `selectionRange` for `declName` where `modsStx` is the modifier part and
|
| 58 |
+
`cmdStx` the remaining part of the syntax tree for `declName`.
|
| 59 |
+
|
| 60 |
+
This method is for the builtin declarations only. User-defined commands should use
|
| 61 |
+
`Lean.Elab.addDeclarationRangesFromSyntax` or `Lean.addDeclarationRanges` to store this information
|
| 62 |
+
for their commands.
|
| 63 |
+
-/
|
| 64 |
+
def addDeclarationRangesForBuiltin [Monad m] [MonadEnv m] [MonadFileMap m] (declName : Name)
|
| 65 |
+
(modsStx : TSyntax ``Parser.Command.declModifiers) (declStx : Syntax) : m Unit := do
|
| 66 |
+
if declStx.getKind == ``Parser.Command.Β«exampleΒ» then
|
| 67 |
+
return ()
|
| 68 |
+
let stx := mkNullNode #[modsStx, declStx]
|
| 69 |
+
addDeclarationRangesFromSyntax declName stx (getDeclarationSelectionRef declStx)
|
| 70 |
+
|
| 71 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/DefView.lean
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Command
|
| 8 |
+
import Lean.Elab.DeclNameGen
|
| 9 |
+
import Lean.Elab.DeclUtil
|
| 10 |
+
|
| 11 |
+
namespace Lean.Elab
|
| 12 |
+
|
| 13 |
+
inductive DefKind where
|
| 14 |
+
| def | instance | theorem | example | opaque | abbrev
|
| 15 |
+
deriving Inhabited, BEq
|
| 16 |
+
|
| 17 |
+
def DefKind.isTheorem : DefKind β Bool
|
| 18 |
+
| .theorem => true
|
| 19 |
+
| _ => false
|
| 20 |
+
|
| 21 |
+
def DefKind.isExample : DefKind β Bool
|
| 22 |
+
| .example => true
|
| 23 |
+
| _ => false
|
| 24 |
+
|
| 25 |
+
/-- Header elaboration data of a `DefView`. -/
|
| 26 |
+
structure DefViewElabHeaderData where
|
| 27 |
+
/--
|
| 28 |
+
Short name. Recall that all declarations in Lean 4 are potentially recursive. We use `shortDeclName` to refer
|
| 29 |
+
to them at `valueStx`, and other declarations in the same mutual block. -/
|
| 30 |
+
shortDeclName : Name
|
| 31 |
+
/-- Full name for this declaration. This is the name that will be added to the `Environment`. -/
|
| 32 |
+
declName : Name
|
| 33 |
+
/-- Universe level parameter names explicitly provided by the user. -/
|
| 34 |
+
levelNames : List Name
|
| 35 |
+
/-- Syntax objects for the binders occurring before `:`, we use them to populate the `InfoTree` when elaborating `valueStx`. -/
|
| 36 |
+
binderIds : Array Syntax
|
| 37 |
+
/-- Number of parameters before `:`, it also includes auto-implicit parameters automatically added by Lean. -/
|
| 38 |
+
numParams : Nat
|
| 39 |
+
/-- Type including parameters. -/
|
| 40 |
+
type : Expr
|
| 41 |
+
deriving Inhabited
|
| 42 |
+
|
| 43 |
+
section Snapshots
|
| 44 |
+
open Language
|
| 45 |
+
|
| 46 |
+
/-- Snapshot after processing of a definition body. -/
|
| 47 |
+
structure BodyProcessedSnapshot extends Language.Snapshot where
|
| 48 |
+
/-- State after elaboration. -/
|
| 49 |
+
state : Term.SavedState
|
| 50 |
+
/-- Elaboration result. -/
|
| 51 |
+
value : Expr
|
| 52 |
+
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
|
| 53 |
+
moreSnaps : Array (SnapshotTask SnapshotTree)
|
| 54 |
+
deriving Nonempty
|
| 55 |
+
instance : Language.ToSnapshotTree BodyProcessedSnapshot where
|
| 56 |
+
toSnapshotTree s := β¨s.toSnapshot, s.moreSnapsβ©
|
| 57 |
+
|
| 58 |
+
/-- Snapshot after elaboration of a definition header. -/
|
| 59 |
+
structure HeaderProcessedSnapshot extends Language.Snapshot where
|
| 60 |
+
/-- Elaboration results. -/
|
| 61 |
+
view : DefViewElabHeaderData
|
| 62 |
+
/-- Resulting elaboration state, including any environment additions. -/
|
| 63 |
+
state : Term.SavedState
|
| 64 |
+
/-- Syntax of top-level tactic block if any, for checking reuse of `tacSnap?`. -/
|
| 65 |
+
tacStx? : Option Syntax
|
| 66 |
+
/-- Incremental execution of main tactic block, if any. -/
|
| 67 |
+
tacSnap? : Option (SnapshotTask Tactic.TacticParsedSnapshot)
|
| 68 |
+
/-- Syntax of definition body, for checking reuse of `bodySnap`. -/
|
| 69 |
+
bodyStx : Syntax
|
| 70 |
+
/-- Result of body elaboration. -/
|
| 71 |
+
bodySnap : SnapshotTask (Option BodyProcessedSnapshot)
|
| 72 |
+
/-- Untyped snapshots from `logSnapshotTask`, saved at this level for cancellation. -/
|
| 73 |
+
moreSnaps : Array (SnapshotTask SnapshotTree)
|
| 74 |
+
deriving Nonempty
|
| 75 |
+
instance : Language.ToSnapshotTree HeaderProcessedSnapshot where
|
| 76 |
+
toSnapshotTree s := β¨s.toSnapshot,
|
| 77 |
+
(match s.tacSnap? with
|
| 78 |
+
| some tac => #[tac.map (sync := true) toSnapshotTree]
|
| 79 |
+
| none => #[]) ++
|
| 80 |
+
#[s.bodySnap.map (sync := true) toSnapshotTree] ++ s.moreSnapsβ©
|
| 81 |
+
|
| 82 |
+
/-- State before elaboration of a mutual definition. -/
|
| 83 |
+
structure DefParsed where
|
| 84 |
+
/--
|
| 85 |
+
Unstructured syntax object comprising the full "header" of the definition from the modifiers
|
| 86 |
+
(incl. docstring) up to the value, used for determining header elaboration reuse.
|
| 87 |
+
-/
|
| 88 |
+
fullHeaderRef : Syntax
|
| 89 |
+
/-- Elaboration result, unless fatal exception occurred. -/
|
| 90 |
+
headerProcessedSnap : SnapshotTask (Option HeaderProcessedSnapshot)
|
| 91 |
+
deriving Nonempty
|
| 92 |
+
|
| 93 |
+
/-- Snapshot after syntax tree has been split into separate mutual def headers. -/
|
| 94 |
+
structure DefsParsedSnapshot extends Language.Snapshot where
|
| 95 |
+
/-- Definitions of this mutual block. -/
|
| 96 |
+
defs : Array DefParsed
|
| 97 |
+
deriving Nonempty, TypeName
|
| 98 |
+
instance : Language.ToSnapshotTree DefsParsedSnapshot where
|
| 99 |
+
toSnapshotTree s := β¨s.toSnapshot,
|
| 100 |
+
s.defs.map (Β·.headerProcessedSnap.map (sync := true) toSnapshotTree)β©
|
| 101 |
+
|
| 102 |
+
end Snapshots
|
| 103 |
+
|
| 104 |
+
structure DefView where
|
| 105 |
+
kind : DefKind
|
| 106 |
+
ref : Syntax
|
| 107 |
+
/--
|
| 108 |
+
An unstructured syntax object that comprises the "header" of the definition, i.e. everything up
|
| 109 |
+
to the value. Used as a more specific ref for header elaboration.
|
| 110 |
+
-/
|
| 111 |
+
headerRef : Syntax
|
| 112 |
+
modifiers : Modifiers
|
| 113 |
+
declId : Syntax
|
| 114 |
+
binders : Syntax
|
| 115 |
+
type? : Option Syntax
|
| 116 |
+
value : Syntax
|
| 117 |
+
/--
|
| 118 |
+
Snapshot for incremental processing of this definition.
|
| 119 |
+
|
| 120 |
+
Invariant: If the bundle's `old?` is set, then elaboration of the header is guaranteed to result
|
| 121 |
+
in the same elaboration result and state, i.e. reuse is possible.
|
| 122 |
+
-/
|
| 123 |
+
headerSnap? : Option (Language.SnapshotBundle (Option HeaderProcessedSnapshot)) := none
|
| 124 |
+
deriving? : Option (Array Syntax) := none
|
| 125 |
+
deriving Inhabited
|
| 126 |
+
|
| 127 |
+
def DefView.isInstance (view : DefView) : Bool :=
|
| 128 |
+
view.modifiers.attrs.any fun attr => attr.name == `instance
|
| 129 |
+
|
| 130 |
+
/-- Prepends the `defeq` attribute, removing existing ones if there are any -/
|
| 131 |
+
def DefView.markDefEq (view : DefView) : DefView :=
|
| 132 |
+
{ view with modifiers :=
|
| 133 |
+
view.modifiers.filterAttrs (Β·.name != `defeq) |>.addFirstAttr { name := `defeq } }
|
| 134 |
+
|
| 135 |
+
namespace Command
|
| 136 |
+
open Meta
|
| 137 |
+
|
| 138 |
+
def mkDefViewOfAbbrev (modifiers : Modifiers) (stx : Syntax) : DefView :=
|
| 139 |
+
-- leading_parser "abbrev " >> declId >> optDeclSig >> declVal
|
| 140 |
+
let (binders, type) := expandOptDeclSig stx[2]
|
| 141 |
+
let modifiers := modifiers.addAttr { name := `inline }
|
| 142 |
+
let modifiers := modifiers.addAttr { name := `reducible }
|
| 143 |
+
{ ref := stx, headerRef := mkNullNode stx.getArgs[*...3], kind := DefKind.abbrev, modifiers,
|
| 144 |
+
declId := stx[1], binders, type? := type, value := stx[3] }
|
| 145 |
+
|
| 146 |
+
def mkDefViewOfDef (modifiers : Modifiers) (stx : Syntax) : DefView :=
|
| 147 |
+
-- leading_parser "def " >> declId >> optDeclSig >> declVal >> optDefDeriving
|
| 148 |
+
let (binders, type) := expandOptDeclSig stx[2]
|
| 149 |
+
let deriving? := if stx[4].isNone then none else some stx[4][1].getSepArgs
|
| 150 |
+
{ ref := stx, headerRef := mkNullNode stx.getArgs[*...3], kind := DefKind.def, modifiers,
|
| 151 |
+
declId := stx[1], binders, type? := type, value := stx[3], deriving? }
|
| 152 |
+
|
| 153 |
+
def mkDefViewOfTheorem (modifiers : Modifiers) (stx : Syntax) : DefView :=
|
| 154 |
+
-- leading_parser "theorem " >> declId >> declSig >> declVal
|
| 155 |
+
let (binders, type) := expandDeclSig stx[2]
|
| 156 |
+
{ ref := stx, headerRef := mkNullNode stx.getArgs[*...3], kind := DefKind.theorem, modifiers,
|
| 157 |
+
declId := stx[1], binders, type? := some type, value := stx[3] }
|
| 158 |
+
|
| 159 |
+
def mkDefViewOfInstance (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView := do
|
| 160 |
+
-- leading_parser Term.attrKind >> "instance " >> optNamedPrio >> optional declId >> declSig >> declVal
|
| 161 |
+
let attrKind β liftMacroM <| toAttributeKind stx[0]
|
| 162 |
+
let prio β liftMacroM <| expandOptNamedPrio stx[2]
|
| 163 |
+
let attrStx β `(attr| instance $(quote prio):num)
|
| 164 |
+
let (binders, type) := expandDeclSig stx[4]
|
| 165 |
+
let modifiers := modifiers.addAttr { kind := attrKind, name := `instance, stx := attrStx }
|
| 166 |
+
let declId β match stx[3].getOptional? with
|
| 167 |
+
| some declId =>
|
| 168 |
+
if β isTracingEnabledFor `Elab.instance.mkInstanceName then
|
| 169 |
+
let id β mkInstanceName binders.getArgs type
|
| 170 |
+
trace[Elab.instance.mkInstanceName] "generated {(β getCurrNamespace) ++ id} for {declId}"
|
| 171 |
+
pure declId
|
| 172 |
+
| none =>
|
| 173 |
+
let id β mkInstanceName binders.getArgs type
|
| 174 |
+
trace[Elab.instance.mkInstanceName] "generated {(β getCurrNamespace) ++ id}"
|
| 175 |
+
pure <| mkNode ``Parser.Command.declId #[mkIdentFrom stx[1] id (canonical := true), mkNullNode]
|
| 176 |
+
return {
|
| 177 |
+
ref := stx, headerRef := mkNullNode stx.getArgs[*...5], kind := DefKind.instance, modifiers := modifiers,
|
| 178 |
+
declId := declId, binders := binders, type? := type, value := stx[5]
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
def mkDefViewOfOpaque (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView := do
|
| 182 |
+
-- leading_parser "opaque " >> declId >> declSig >> optional declValSimple
|
| 183 |
+
let (binders, type) := expandDeclSig stx[2]
|
| 184 |
+
let val β match stx[3].getOptional? with
|
| 185 |
+
| some val => pure val
|
| 186 |
+
| none =>
|
| 187 |
+
let val β if modifiers.isUnsafe then `(default_or_ofNonempty% unsafe) else `(default_or_ofNonempty%)
|
| 188 |
+
`(Parser.Command.declValSimple| := $val)
|
| 189 |
+
return {
|
| 190 |
+
ref := stx, headerRef := mkNullNode stx.getArgs[*...3], kind := DefKind.opaque, modifiers := modifiers,
|
| 191 |
+
declId := stx[1], binders := binders, type? := some type, value := val
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
def mkDefViewOfExample (modifiers : Modifiers) (stx : Syntax) : DefView :=
|
| 195 |
+
-- leading_parser "example " >> declSig >> declVal
|
| 196 |
+
let (binders, type) := expandOptDeclSig stx[1]
|
| 197 |
+
let id := mkIdentFrom stx[0] `_example (canonical := true)
|
| 198 |
+
let declId := mkNode ``Parser.Command.declId #[id, mkNullNode]
|
| 199 |
+
{ ref := stx, headerRef := mkNullNode stx.getArgs[*...2], kind := DefKind.example, modifiers := modifiers,
|
| 200 |
+
declId := declId, binders := binders, type? := type, value := stx[2] }
|
| 201 |
+
|
| 202 |
+
def isDefLike (stx : Syntax) : Bool :=
|
| 203 |
+
let declKind := stx.getKind
|
| 204 |
+
declKind == ``Parser.Command.abbrev ||
|
| 205 |
+
declKind == ``Parser.Command.definition ||
|
| 206 |
+
declKind == ``Parser.Command.theorem ||
|
| 207 |
+
declKind == ``Parser.Command.opaque ||
|
| 208 |
+
declKind == ``Parser.Command.instance ||
|
| 209 |
+
declKind == ``Parser.Command.example
|
| 210 |
+
|
| 211 |
+
def mkDefView (modifiers : Modifiers) (stx : Syntax) : CommandElabM DefView :=
|
| 212 |
+
let declKind := stx.getKind
|
| 213 |
+
if declKind == ``Parser.Command.Β«abbrevΒ» then
|
| 214 |
+
return mkDefViewOfAbbrev modifiers stx
|
| 215 |
+
else if declKind == ``Parser.Command.definition then
|
| 216 |
+
return mkDefViewOfDef modifiers stx
|
| 217 |
+
else if declKind == ``Parser.Command.theorem then
|
| 218 |
+
return mkDefViewOfTheorem modifiers stx
|
| 219 |
+
else if declKind == ``Parser.Command.opaque then
|
| 220 |
+
mkDefViewOfOpaque modifiers stx
|
| 221 |
+
else if declKind == ``Parser.Command.instance then
|
| 222 |
+
mkDefViewOfInstance modifiers stx
|
| 223 |
+
else if declKind == ``Parser.Command.example then
|
| 224 |
+
return mkDefViewOfExample modifiers stx
|
| 225 |
+
else
|
| 226 |
+
throwError "unexpected kind of definition"
|
| 227 |
+
|
| 228 |
+
builtin_initialize registerTraceClass `Elab.definition
|
| 229 |
+
builtin_initialize registerTraceClass `Elab.instance.mkInstanceName
|
| 230 |
+
|
| 231 |
+
end Command
|
| 232 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Deriving.lean
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Deriving.Basic
|
| 8 |
+
import Lean.Elab.Deriving.Util
|
| 9 |
+
import Lean.Elab.Deriving.Inhabited
|
| 10 |
+
import Lean.Elab.Deriving.Nonempty
|
| 11 |
+
import Lean.Elab.Deriving.TypeName
|
| 12 |
+
import Lean.Elab.Deriving.BEq
|
| 13 |
+
import Lean.Elab.Deriving.DecEq
|
| 14 |
+
import Lean.Elab.Deriving.Repr
|
| 15 |
+
import Lean.Elab.Deriving.FromToJson
|
| 16 |
+
import Lean.Elab.Deriving.SizeOf
|
| 17 |
+
import Lean.Elab.Deriving.Hashable
|
| 18 |
+
import Lean.Elab.Deriving.Ord
|
| 19 |
+
import Lean.Elab.Deriving.ToExpr
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Do.lean
ADDED
|
@@ -0,0 +1,1827 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Term
|
| 8 |
+
import Lean.Elab.BindersUtil
|
| 9 |
+
import Lean.Elab.PatternVar
|
| 10 |
+
import Lean.Elab.Quotation.Util
|
| 11 |
+
import Lean.Parser.Do
|
| 12 |
+
|
| 13 |
+
-- HACK: avoid code explosion until heuristics are improved
|
| 14 |
+
set_option compiler.reuse false
|
| 15 |
+
|
| 16 |
+
namespace Lean.Elab.Term
|
| 17 |
+
open Lean.Parser.Term
|
| 18 |
+
open Meta
|
| 19 |
+
open TSyntax.Compat
|
| 20 |
+
|
| 21 |
+
private def getDoSeqElems (doSeq : Syntax) : List Syntax :=
|
| 22 |
+
if doSeq.getKind == ``Parser.Term.doSeqBracketed then
|
| 23 |
+
doSeq[1].getArgs.toList.map fun arg => arg[0]
|
| 24 |
+
else if doSeq.getKind == ``Parser.Term.doSeqIndent then
|
| 25 |
+
doSeq[0].getArgs.toList.map fun arg => arg[0]
|
| 26 |
+
else
|
| 27 |
+
[]
|
| 28 |
+
|
| 29 |
+
private def getDoSeq (doStx : Syntax) : Syntax :=
|
| 30 |
+
doStx[1]
|
| 31 |
+
|
| 32 |
+
@[builtin_term_elab liftMethod] def elabLiftMethod : TermElab := fun stx _ =>
|
| 33 |
+
throwErrorAt stx "invalid use of `(<- ...)`, must be nested inside a 'do' expression"
|
| 34 |
+
|
| 35 |
+
/-- Return true if we should not lift `(<- ...)` actions nested in the syntax nodes with the given kind. -/
|
| 36 |
+
private def liftMethodDelimiter (k : SyntaxNodeKind) : Bool :=
|
| 37 |
+
k == ``Parser.Term.do ||
|
| 38 |
+
k == ``Parser.Term.doSeqIndent ||
|
| 39 |
+
k == ``Parser.Term.doSeqBracketed ||
|
| 40 |
+
k == ``Parser.Term.termReturn ||
|
| 41 |
+
k == ``Parser.Term.termUnless ||
|
| 42 |
+
k == ``Parser.Term.termTry ||
|
| 43 |
+
k == ``Parser.Term.termFor
|
| 44 |
+
|
| 45 |
+
/-- Given `stx` which is a `letPatDecl`, `letEqnsDecl`, or `letIdDecl`, return true if it has binders. -/
|
| 46 |
+
private def letDeclArgHasBinders (letDeclArg : Syntax) : Bool :=
|
| 47 |
+
let k := letDeclArg.getKind
|
| 48 |
+
if k == ``Parser.Term.letPatDecl then
|
| 49 |
+
false
|
| 50 |
+
else if k == ``Parser.Term.letEqnsDecl then
|
| 51 |
+
true
|
| 52 |
+
else if k == ``Parser.Term.letIdDecl then
|
| 53 |
+
-- letIdLhs := binderIdent >> checkWsBefore "expected space before binders" >> many (ppSpace >> letIdBinder)) >> optType
|
| 54 |
+
let binders := letDeclArg[1]
|
| 55 |
+
binders.getNumArgs > 0
|
| 56 |
+
else
|
| 57 |
+
false
|
| 58 |
+
|
| 59 |
+
/-- Return `true` if the given `letDecl` contains binders. -/
|
| 60 |
+
private def letDeclHasBinders (letDecl : Syntax) : Bool :=
|
| 61 |
+
letDeclArgHasBinders letDecl[0]
|
| 62 |
+
|
| 63 |
+
/-- Return true if we should generate an error message when lifting a method over this kind of syntax. -/
|
| 64 |
+
private def liftMethodForbiddenBinder (stx : Syntax) : Bool :=
|
| 65 |
+
let k := stx.getKind
|
| 66 |
+
-- TODO: make this extensible in the future.
|
| 67 |
+
if k == ``Parser.Term.fun || k == ``Parser.Term.matchAlts ||
|
| 68 |
+
k == ``Parser.Term.doLetRec || k == ``Parser.Term.letrec then
|
| 69 |
+
-- It is never ok to lift over this kind of binder
|
| 70 |
+
true
|
| 71 |
+
-- The following kinds of `let`-expressions require extra checks to decide whether they contain binders or not
|
| 72 |
+
else if k == ``Parser.Term.let then
|
| 73 |
+
letDeclHasBinders stx[1]
|
| 74 |
+
else if k == ``Parser.Term.doLet then
|
| 75 |
+
letDeclHasBinders stx[2]
|
| 76 |
+
else if k == ``Parser.Term.doLetArrow then
|
| 77 |
+
letDeclArgHasBinders stx[2]
|
| 78 |
+
else
|
| 79 |
+
false
|
| 80 |
+
|
| 81 |
+
-- TODO: we must track whether we are inside a quotation or not.
|
| 82 |
+
private partial def hasLiftMethod : Syntax β Bool
|
| 83 |
+
| Syntax.node _ k args =>
|
| 84 |
+
if liftMethodDelimiter k then false
|
| 85 |
+
-- NOTE: We don't check for lifts in quotations here, which doesn't break anything but merely makes this rare case a
|
| 86 |
+
-- bit slower
|
| 87 |
+
else if k == ``Parser.Term.liftMethod then true
|
| 88 |
+
-- For `pure` if-then-else, we only lift `(<- ...)` occurring in the condition.
|
| 89 |
+
else if k == ``termDepIfThenElse || k == ``termIfThenElse then args.size >= 2 && hasLiftMethod args[1]!
|
| 90 |
+
else args.any hasLiftMethod
|
| 91 |
+
| _ => false
|
| 92 |
+
|
| 93 |
+
structure ExtractMonadResult where
|
| 94 |
+
m : Expr
|
| 95 |
+
returnType : Expr
|
| 96 |
+
expectedType : Expr
|
| 97 |
+
|
| 98 |
+
private def mkUnknownMonadResult : MetaM ExtractMonadResult := do
|
| 99 |
+
let u β mkFreshLevelMVar
|
| 100 |
+
let v β mkFreshLevelMVar
|
| 101 |
+
let m β mkFreshExprMVar (β mkArrow (mkSort (mkLevelSucc u)) (mkSort (mkLevelSucc v)))
|
| 102 |
+
let returnType β mkFreshExprMVar (mkSort (mkLevelSucc u))
|
| 103 |
+
return { m, returnType, expectedType := mkApp m returnType }
|
| 104 |
+
|
| 105 |
+
private partial def extractBind (expectedType? : Option Expr) : TermElabM ExtractMonadResult := do
|
| 106 |
+
let some expectedType := expectedType? | mkUnknownMonadResult
|
| 107 |
+
let extractStep? (type : Expr) : MetaM (Option ExtractMonadResult) := do
|
| 108 |
+
let .app m returnType := type | return none
|
| 109 |
+
try
|
| 110 |
+
let bindInstType β mkAppM ``Bind #[m]
|
| 111 |
+
discard <| Meta.synthInstance bindInstType
|
| 112 |
+
return some { m, returnType, expectedType }
|
| 113 |
+
catch _ =>
|
| 114 |
+
return none
|
| 115 |
+
let rec extract? (type : Expr) : MetaM (Option ExtractMonadResult) := do
|
| 116 |
+
match (β extractStep? type) with
|
| 117 |
+
| some r => return r
|
| 118 |
+
| none =>
|
| 119 |
+
let typeNew β whnfCore type
|
| 120 |
+
if typeNew != type then
|
| 121 |
+
extract? typeNew
|
| 122 |
+
else
|
| 123 |
+
if typeNew.getAppFn.isMVar then
|
| 124 |
+
mkUnknownMonadResult
|
| 125 |
+
else match (β unfoldDefinition? typeNew) with
|
| 126 |
+
| some typeNew => extract? typeNew
|
| 127 |
+
| none => return none
|
| 128 |
+
match (β extract? expectedType) with
|
| 129 |
+
| some r => return r
|
| 130 |
+
| none => throwError "invalid `do` notation, expected type is not a monad application{indentExpr expectedType}\nYou can use the `do` notation in pure code by writing `Id.run do` instead of `do`, where `Id` is the identity monad."
|
| 131 |
+
|
| 132 |
+
namespace Do
|
| 133 |
+
|
| 134 |
+
abbrev Var := Syntax -- TODO: should be `Ident`
|
| 135 |
+
|
| 136 |
+
/-- A `doMatch` alternative. `vars` is the array of variables declared by `patterns`. -/
|
| 137 |
+
structure Alt (Ο : Type) where
|
| 138 |
+
ref : Syntax
|
| 139 |
+
vars : Array Var
|
| 140 |
+
patterns : Syntax
|
| 141 |
+
rhs : Ο
|
| 142 |
+
deriving Inhabited
|
| 143 |
+
|
| 144 |
+
/-- A `doMatchExpr` alternative. -/
|
| 145 |
+
structure AltExpr (Ο : Type) where
|
| 146 |
+
ref : Syntax
|
| 147 |
+
var? : Option Var
|
| 148 |
+
funName : Syntax
|
| 149 |
+
pvars : Array Syntax
|
| 150 |
+
rhs : Ο
|
| 151 |
+
deriving Inhabited
|
| 152 |
+
|
| 153 |
+
def AltExpr.vars (alt : AltExpr Ο) : Array Var := Id.run do
|
| 154 |
+
let mut vars := #[]
|
| 155 |
+
if let some var := alt.var? then
|
| 156 |
+
vars := vars.push var
|
| 157 |
+
for pvar in alt.pvars do
|
| 158 |
+
match pvar with
|
| 159 |
+
| `(_) => pure ()
|
| 160 |
+
| _ => vars := vars.push pvar
|
| 161 |
+
return vars
|
| 162 |
+
|
| 163 |
+
/--
|
| 164 |
+
Auxiliary datastructure for representing a `do` code block, and compiling "reassignments" (e.g., `x := x + 1`).
|
| 165 |
+
We convert `Code` into a `Syntax` term representing the:
|
| 166 |
+
- `do`-block, or
|
| 167 |
+
- the visitor argument for the `forIn` combinator.
|
| 168 |
+
|
| 169 |
+
We say the following constructors are terminals:
|
| 170 |
+
- `break`: for interrupting a `for x in s`
|
| 171 |
+
- `continue`: for interrupting the current iteration of a `for x in s`
|
| 172 |
+
- `return e`: for returning `e` as the result for the whole `do` computation block
|
| 173 |
+
- `action a`: for executing action `a` as a terminal
|
| 174 |
+
- `ite`: if-then-else
|
| 175 |
+
- `match`: pattern matching
|
| 176 |
+
- `jmp` a goto to a join-point
|
| 177 |
+
|
| 178 |
+
We say the terminals `break`, `continue`, `action`, and `return` are "exit points"
|
| 179 |
+
|
| 180 |
+
Note that, `return e` is not equivalent to `action (pure e)`. Here is an example:
|
| 181 |
+
```
|
| 182 |
+
def f (x : Nat) : IO Unit := do
|
| 183 |
+
if x == 0 then
|
| 184 |
+
return ()
|
| 185 |
+
IO.println "hello"
|
| 186 |
+
```
|
| 187 |
+
Executing `#eval f 0` will not print "hello". Now, consider
|
| 188 |
+
```
|
| 189 |
+
def g (x : Nat) : IO Unit := do
|
| 190 |
+
if x == 0 then
|
| 191 |
+
pure ()
|
| 192 |
+
IO.println "hello"
|
| 193 |
+
```
|
| 194 |
+
The `if` statement is essentially a noop, and "hello" is printed when we execute `g 0`.
|
| 195 |
+
|
| 196 |
+
- `decl` represents all declaration-like `doElem`s (e.g., `let`, `have`, `let rec`).
|
| 197 |
+
The field `stx` is the actual `doElem`,
|
| 198 |
+
`vars` is the array of variables declared by it, and `cont` is the next instruction in the `do` code block.
|
| 199 |
+
`vars` is an array since we have declarations such as `let (a, b) := s`.
|
| 200 |
+
|
| 201 |
+
- `reassign` is an reassignment-like `doElem` (e.g., `x := x + 1`).
|
| 202 |
+
|
| 203 |
+
- `joinpoint` is a join point declaration: an auxiliary `let`-declaration used to represent the control-flow.
|
| 204 |
+
|
| 205 |
+
- `seq a k` executes action `a`, ignores its result, and then executes `k`.
|
| 206 |
+
We also store the do-elements `dbg_trace` and `assert!` as actions in a `seq`.
|
| 207 |
+
|
| 208 |
+
A code block `C` is well-formed if
|
| 209 |
+
- For every `jmp ref j as` in `C`, there is a `joinpoint j ps b k` and `jmp ref j as` is in `k`, and
|
| 210 |
+
`ps.size == as.size` -/
|
| 211 |
+
inductive Code where
|
| 212 |
+
| decl (xs : Array Var) (doElem : Syntax) (k : Code)
|
| 213 |
+
| reassign (xs : Array Var) (doElem : Syntax) (k : Code)
|
| 214 |
+
/-- The Boolean value in `params` indicates whether we should use `(x : typeof! x)` when generating term Syntax or not -/
|
| 215 |
+
| joinpoint (name : Name) (params : Array (Var Γ Bool)) (body : Code) (k : Code)
|
| 216 |
+
| seq (action : Syntax) (k : Code)
|
| 217 |
+
| action (action : Syntax)
|
| 218 |
+
| break (ref : Syntax)
|
| 219 |
+
| continue (ref : Syntax)
|
| 220 |
+
| return (ref : Syntax) (val : Syntax)
|
| 221 |
+
/-- Recall that an if-then-else may declare a variable using `optIdent` for the branches `thenBranch` and `elseBranch`. We store the variable name at `var?`. -/
|
| 222 |
+
| ite (ref : Syntax) (h? : Option Var) (optIdent : Syntax) (cond : Syntax) (thenBranch : Code) (elseBranch : Code)
|
| 223 |
+
| match (ref : Syntax) (gen : Syntax) (discrs : Syntax) (optMotive : Syntax) (alts : Array (Alt Code))
|
| 224 |
+
| matchExpr (ref : Syntax) (Β«metaΒ» : Bool) (discr : Syntax) (alts : Array (AltExpr Code)) (elseBranch : Code)
|
| 225 |
+
| jmp (ref : Syntax) (jpName : Name) (args : Array Syntax)
|
| 226 |
+
deriving Inhabited
|
| 227 |
+
|
| 228 |
+
def Code.getRef? : Code β Option Syntax
|
| 229 |
+
| .decl _ doElem _ => doElem
|
| 230 |
+
| .reassign _ doElem _ => doElem
|
| 231 |
+
| .joinpoint .. => none
|
| 232 |
+
| .seq a _ => a
|
| 233 |
+
| .action a => a
|
| 234 |
+
| .break ref => ref
|
| 235 |
+
| .continue ref => ref
|
| 236 |
+
| .return ref _ => ref
|
| 237 |
+
| .ite ref .. => ref
|
| 238 |
+
| .match ref .. => ref
|
| 239 |
+
| .matchExpr ref .. => ref
|
| 240 |
+
| .jmp ref .. => ref
|
| 241 |
+
|
| 242 |
+
abbrev VarSet := RBMap Name Syntax Name.cmp
|
| 243 |
+
|
| 244 |
+
/-- A code block, and the collection of variables updated by it. -/
|
| 245 |
+
structure CodeBlock where
|
| 246 |
+
code : Code
|
| 247 |
+
uvars : VarSet := {} -- set of variables updated by `code`
|
| 248 |
+
|
| 249 |
+
private def varSetToArray (s : VarSet) : Array Var :=
|
| 250 |
+
s.fold (fun xs _ x => xs.push x) #[]
|
| 251 |
+
|
| 252 |
+
private def varsToMessageData (vars : Array Var) : MessageData :=
|
| 253 |
+
MessageData.joinSep (vars.toList.map fun n => MessageData.ofName (n.getId.simpMacroScopes)) " "
|
| 254 |
+
|
| 255 |
+
partial def CodeBlocl.toMessageData (codeBlock : CodeBlock) : MessageData :=
|
| 256 |
+
let us := MessageData.ofList <| (varSetToArray codeBlock.uvars).toList.map MessageData.ofSyntax
|
| 257 |
+
let rec loop : Code β MessageData
|
| 258 |
+
| .decl xs _ k => m!"let {varsToMessageData xs} := ...\n{loop k}"
|
| 259 |
+
| .reassign xs _ k => m!"{varsToMessageData xs} := ...\n{loop k}"
|
| 260 |
+
| .joinpoint n ps body k => m!"let {n.simpMacroScopes} {varsToMessageData (ps.map Prod.fst)} := {indentD (loop body)}\n{loop k}"
|
| 261 |
+
| .seq e k => m!"{e}\n{loop k}"
|
| 262 |
+
| .action e => e
|
| 263 |
+
| .ite _ _ _ c t e => m!"if {c} then {indentD (loop t)}\nelse{loop e}"
|
| 264 |
+
| .jmp _ j xs => m!"jmp {j.simpMacroScopes} {xs.toList}"
|
| 265 |
+
| .break _ => m!"break {us}"
|
| 266 |
+
| .continue _ => m!"continue {us}"
|
| 267 |
+
| .return _ v => m!"return {v} {us}"
|
| 268 |
+
| .match _ _ ds _ alts =>
|
| 269 |
+
m!"match {ds} with"
|
| 270 |
+
++ alts.foldl (init := m!"") fun acc alt => acc ++ m!"\n| {alt.patterns} => {loop alt.rhs}"
|
| 271 |
+
| .matchExpr _ Β«metaΒ» d alts elseCode =>
|
| 272 |
+
let r := m!"match_expr {if Β«metaΒ» then "" else "(meta := false)"} {d} with"
|
| 273 |
+
let r := r ++ alts.foldl (init := m!"") fun acc alt =>
|
| 274 |
+
let acc := acc ++ m!"\n| {if let some var := alt.var? then m!"{var}@" else ""}"
|
| 275 |
+
let acc := acc ++ m!"{alt.funName}"
|
| 276 |
+
let acc := acc ++ alt.pvars.foldl (init := m!"") fun acc pvar => acc ++ m!" {pvar}"
|
| 277 |
+
acc ++ m!" => {loop alt.rhs}"
|
| 278 |
+
r ++ m!"| _ => {loop elseCode}"
|
| 279 |
+
loop codeBlock.code
|
| 280 |
+
|
| 281 |
+
/-- Return true if the give code contains an exit point that satisfies `p` -/
|
| 282 |
+
partial def hasExitPointPred (c : Code) (p : Code β Bool) : Bool :=
|
| 283 |
+
let rec loop : Code β Bool
|
| 284 |
+
| .decl _ _ k => loop k
|
| 285 |
+
| .reassign _ _ k => loop k
|
| 286 |
+
| .joinpoint _ _ b k => loop b || loop k
|
| 287 |
+
| .seq _ k => loop k
|
| 288 |
+
| .ite _ _ _ _ t e => loop t || loop e
|
| 289 |
+
| .match _ _ _ _ alts => alts.any (loop Β·.rhs)
|
| 290 |
+
| .matchExpr _ _ _ alts e => alts.any (loop Β·.rhs) || loop e
|
| 291 |
+
| .jmp .. => false
|
| 292 |
+
| c => p c
|
| 293 |
+
loop c
|
| 294 |
+
|
| 295 |
+
def hasExitPoint (c : Code) : Bool :=
|
| 296 |
+
hasExitPointPred c fun _ => true
|
| 297 |
+
|
| 298 |
+
def hasReturn (c : Code) : Bool :=
|
| 299 |
+
hasExitPointPred c fun
|
| 300 |
+
| .return .. => true
|
| 301 |
+
| _ => false
|
| 302 |
+
|
| 303 |
+
def hasTerminalAction (c : Code) : Bool :=
|
| 304 |
+
hasExitPointPred c fun
|
| 305 |
+
| .action _ => true
|
| 306 |
+
| _ => false
|
| 307 |
+
|
| 308 |
+
def hasBreakContinue (c : Code) : Bool :=
|
| 309 |
+
hasExitPointPred c fun
|
| 310 |
+
| .break _ => true
|
| 311 |
+
| .continue _ => true
|
| 312 |
+
| _ => false
|
| 313 |
+
|
| 314 |
+
def hasBreakContinueReturn (c : Code) : Bool :=
|
| 315 |
+
hasExitPointPred c fun
|
| 316 |
+
| .break _ => true
|
| 317 |
+
| .continue _ => true
|
| 318 |
+
| .return _ _ => true
|
| 319 |
+
| _ => false
|
| 320 |
+
|
| 321 |
+
def mkAuxDeclFor {m} [Monad m] [MonadQuotation m] (e : Syntax) (mkCont : Syntax β m Code) : m Code := withRef e <| withFreshMacroScope do
|
| 322 |
+
let y β `(y)
|
| 323 |
+
let doElem β `(doElem| let y β $e:term)
|
| 324 |
+
-- Add elaboration hint for producing sane error message
|
| 325 |
+
let y β `(ensure_expected_type% "type mismatch, result value" $y)
|
| 326 |
+
let k β mkCont y
|
| 327 |
+
return .decl #[y] doElem k
|
| 328 |
+
|
| 329 |
+
/-- Convert `action _ e` instructions in `c` into `let y β e; jmp _ jp (xs y)`. -/
|
| 330 |
+
partial def convertTerminalActionIntoJmp (code : Code) (jp : Name) (xs : Array Var) : MacroM Code :=
|
| 331 |
+
let rec loop : Code β MacroM Code
|
| 332 |
+
| .decl xs stx k => return .decl xs stx (β loop k)
|
| 333 |
+
| .reassign xs stx k => return .reassign xs stx (β loop k)
|
| 334 |
+
| .joinpoint n ps b k => return .joinpoint n ps (β loop b) (β loop k)
|
| 335 |
+
| .seq e k => return .seq e (β loop k)
|
| 336 |
+
| .ite ref x? h c t e => return .ite ref x? h c (β loop t) (β loop e)
|
| 337 |
+
| .action e => mkAuxDeclFor e fun y =>
|
| 338 |
+
let ref := e
|
| 339 |
+
-- We jump to `jp` with xs **and** y
|
| 340 |
+
let jmpArgs := xs.push y
|
| 341 |
+
return Code.jmp ref jp jmpArgs
|
| 342 |
+
| .match ref g ds t alts =>
|
| 343 |
+
return .match ref g ds t (β alts.mapM fun alt => do pure { alt with rhs := (β loop alt.rhs) })
|
| 344 |
+
| .matchExpr ref Β«metaΒ» d alts e => do
|
| 345 |
+
let alts β alts.mapM fun alt => do pure { alt with rhs := (β loop alt.rhs) }
|
| 346 |
+
let e β loop e
|
| 347 |
+
return .matchExpr ref Β«metaΒ» d alts e
|
| 348 |
+
| c => return c
|
| 349 |
+
loop code
|
| 350 |
+
|
| 351 |
+
structure JPDecl where
|
| 352 |
+
name : Name
|
| 353 |
+
params : Array (Var Γ Bool)
|
| 354 |
+
body : Code
|
| 355 |
+
|
| 356 |
+
def attachJP (jpDecl : JPDecl) (k : Code) : Code :=
|
| 357 |
+
Code.joinpoint jpDecl.name jpDecl.params jpDecl.body k
|
| 358 |
+
|
| 359 |
+
def attachJPs (jpDecls : Array JPDecl) (k : Code) : Code :=
|
| 360 |
+
jpDecls.foldr attachJP k
|
| 361 |
+
|
| 362 |
+
def mkFreshJP (ps : Array (Var Γ Bool)) (body : Code) : TermElabM JPDecl := do
|
| 363 |
+
let ps β if ps.isEmpty then
|
| 364 |
+
let y β `(y)
|
| 365 |
+
pure #[(y.raw, false)]
|
| 366 |
+
else
|
| 367 |
+
pure ps
|
| 368 |
+
-- Remark: the compiler frontend implemented in C++ currently detects jointpoints created by
|
| 369 |
+
-- the "do" notation by testing the name. See hack at method `visit_let` at `lcnf.cpp`
|
| 370 |
+
-- We will remove this hack when we re-implement the compiler frontend in Lean.
|
| 371 |
+
let name β mkFreshUserName `__do_jp
|
| 372 |
+
pure { name := name, params := ps, body := body }
|
| 373 |
+
|
| 374 |
+
def addFreshJP (ps : Array (Var Γ Bool)) (body : Code) : StateRefT (Array JPDecl) TermElabM Name := do
|
| 375 |
+
let jp β mkFreshJP ps body
|
| 376 |
+
modify fun (jps : Array JPDecl) => jps.push jp
|
| 377 |
+
pure jp.name
|
| 378 |
+
|
| 379 |
+
def insertVars (rs : VarSet) (xs : Array Var) : VarSet :=
|
| 380 |
+
xs.foldl (fun rs x => rs.insert x.getId x) rs
|
| 381 |
+
|
| 382 |
+
def eraseVars (rs : VarSet) (xs : Array Var) : VarSet :=
|
| 383 |
+
xs.foldl (Β·.erase Β·.getId) rs
|
| 384 |
+
|
| 385 |
+
def eraseOptVar (rs : VarSet) (x? : Option Var) : VarSet :=
|
| 386 |
+
match x? with
|
| 387 |
+
| none => rs
|
| 388 |
+
| some x => rs.insert x.getId x
|
| 389 |
+
|
| 390 |
+
/-- Create a new jointpoint for `c`, and jump to it with the variables `rs` -/
|
| 391 |
+
def mkSimpleJmp (ref : Syntax) (rs : VarSet) (c : Code) : StateRefT (Array JPDecl) TermElabM Code := do
|
| 392 |
+
let xs := varSetToArray rs
|
| 393 |
+
let jp β addFreshJP (xs.map fun x => (x, true)) c
|
| 394 |
+
if xs.isEmpty then
|
| 395 |
+
let unit β ``(Unit.unit)
|
| 396 |
+
return Code.jmp ref jp #[unit]
|
| 397 |
+
else
|
| 398 |
+
return Code.jmp ref jp xs
|
| 399 |
+
|
| 400 |
+
/-- Create a new joinpoint that takes `rs` and `val` as arguments. `val` must be syntax representing a pure value.
|
| 401 |
+
The body of the joinpoint is created using `mkJPBody yFresh`, where `yFresh`
|
| 402 |
+
is a fresh variable created by this method. -/
|
| 403 |
+
def mkJmp (ref : Syntax) (rs : VarSet) (val : Syntax) (mkJPBody : Syntax β MacroM Code) : StateRefT (Array JPDecl) TermElabM Code := do
|
| 404 |
+
let xs := varSetToArray rs
|
| 405 |
+
let args := xs.push val
|
| 406 |
+
let yFresh β withRef ref `(y)
|
| 407 |
+
let ps := xs.map fun x => (x, true)
|
| 408 |
+
let ps := ps.push (yFresh, false)
|
| 409 |
+
let jpBody β liftMacroM <| mkJPBody yFresh
|
| 410 |
+
let jp β addFreshJP ps jpBody
|
| 411 |
+
return Code.jmp ref jp args
|
| 412 |
+
|
| 413 |
+
/-- `pullExitPointsAux rs c` auxiliary method for `pullExitPoints`, `rs` is the set of update variable in the current path. -/
|
| 414 |
+
partial def pullExitPointsAux (rs : VarSet) (c : Code) : StateRefT (Array JPDecl) TermElabM Code := do
|
| 415 |
+
match c with
|
| 416 |
+
| .decl xs stx k => return .decl xs stx (β pullExitPointsAux (eraseVars rs xs) k)
|
| 417 |
+
| .reassign xs stx k => return .reassign xs stx (β pullExitPointsAux (insertVars rs xs) k)
|
| 418 |
+
| .joinpoint j ps b k => return .joinpoint j ps (β pullExitPointsAux rs b) (β pullExitPointsAux rs k)
|
| 419 |
+
| .seq e k => return .seq e (β pullExitPointsAux rs k)
|
| 420 |
+
| .ite ref x? o c t e => return .ite ref x? o c (β pullExitPointsAux (eraseOptVar rs x?) t) (β pullExitPointsAux (eraseOptVar rs x?) e)
|
| 421 |
+
| .jmp .. => return c
|
| 422 |
+
| .break ref => mkSimpleJmp ref rs (.break ref)
|
| 423 |
+
| .continue ref => mkSimpleJmp ref rs (.continue ref)
|
| 424 |
+
| .return ref val => mkJmp ref rs val (fun y => return .return ref y)
|
| 425 |
+
| .action e =>
|
| 426 |
+
-- We use `mkAuxDeclFor` because `e` is not pure.
|
| 427 |
+
mkAuxDeclFor e fun y =>
|
| 428 |
+
let ref := e
|
| 429 |
+
mkJmp ref rs y (fun yFresh => return .action (β ``(Pure.pure $yFresh)))
|
| 430 |
+
| .match ref g ds t alts =>
|
| 431 |
+
let alts β alts.mapM fun alt => do pure { alt with rhs := (β pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) }
|
| 432 |
+
return .match ref g ds t alts
|
| 433 |
+
| .matchExpr ref Β«metaΒ» d alts e =>
|
| 434 |
+
let alts β alts.mapM fun alt => do pure { alt with rhs := (β pullExitPointsAux (eraseVars rs alt.vars) alt.rhs) }
|
| 435 |
+
let e β pullExitPointsAux rs e
|
| 436 |
+
return .matchExpr ref Β«metaΒ» d alts e
|
| 437 |
+
|
| 438 |
+
/--
|
| 439 |
+
Auxiliary operation for adding new variables to the collection of updated variables in a CodeBlock.
|
| 440 |
+
When a new variable is not already in the collection, but is shadowed by some declaration in `c`,
|
| 441 |
+
we create auxiliary join points to make sure we preserve the semantics of the code block.
|
| 442 |
+
Example: suppose we have the code block `print x; let x := 10; return x`. And we want to extend it
|
| 443 |
+
with the reassignment `x := x + 1`. We first use `pullExitPoints` to create
|
| 444 |
+
```
|
| 445 |
+
let jp (x!1) := return x!1;
|
| 446 |
+
print x;
|
| 447 |
+
let x := 10;
|
| 448 |
+
jmp jp x
|
| 449 |
+
```
|
| 450 |
+
and then we add the reassignment
|
| 451 |
+
```
|
| 452 |
+
x := x + 1
|
| 453 |
+
let jp (x!1) := return x!1;
|
| 454 |
+
print x;
|
| 455 |
+
let x := 10;
|
| 456 |
+
jmp jp x
|
| 457 |
+
```
|
| 458 |
+
Note that we created a fresh variable `x!1` to avoid accidental name capture.
|
| 459 |
+
As another example, consider
|
| 460 |
+
```
|
| 461 |
+
print x;
|
| 462 |
+
let x := 10
|
| 463 |
+
y := y + 1;
|
| 464 |
+
return x;
|
| 465 |
+
```
|
| 466 |
+
We transform it into
|
| 467 |
+
```
|
| 468 |
+
let jp (y x!1) := return x!1;
|
| 469 |
+
print x;
|
| 470 |
+
let x := 10
|
| 471 |
+
y := y + 1;
|
| 472 |
+
jmp jp y x
|
| 473 |
+
```
|
| 474 |
+
and then we add the reassignment as in the previous example.
|
| 475 |
+
We need to include `y` in the jump, because each exit point is implicitly returning the set of
|
| 476 |
+
update variables.
|
| 477 |
+
|
| 478 |
+
We implement the method as follows. Let `us` be `c.uvars`, then
|
| 479 |
+
1- for each `return _ y` in `c`, we create a join point
|
| 480 |
+
`let j (us y!1) := return y!1`
|
| 481 |
+
and replace the `return _ y` with `jmp us y`
|
| 482 |
+
2- for each `break`, we create a join point
|
| 483 |
+
`let j (us) := break`
|
| 484 |
+
and replace the `break` with `jmp us`.
|
| 485 |
+
3- Same as 2 for `continue`.
|
| 486 |
+
-/
|
| 487 |
+
def pullExitPoints (c : Code) : TermElabM Code := do
|
| 488 |
+
if hasExitPoint c then
|
| 489 |
+
let (c, jpDecls) β (pullExitPointsAux {} c).run #[]
|
| 490 |
+
return attachJPs jpDecls c
|
| 491 |
+
else
|
| 492 |
+
return c
|
| 493 |
+
|
| 494 |
+
partial def extendUpdatedVarsAux (c : Code) (ws : VarSet) : TermElabM Code :=
|
| 495 |
+
let rec update (c : Code) : TermElabM Code := do
|
| 496 |
+
match c with
|
| 497 |
+
| .joinpoint j ps b k => return .joinpoint j ps (β update b) (β update k)
|
| 498 |
+
| .seq e k => return .seq e (β update k)
|
| 499 |
+
| .match ref g ds t alts =>
|
| 500 |
+
if alts.any fun alt => alt.vars.any fun x => ws.contains x.getId then
|
| 501 |
+
-- If a pattern variable is shadowing a variable in ws, we `pullExitPoints`
|
| 502 |
+
pullExitPoints c
|
| 503 |
+
else
|
| 504 |
+
return .match ref g ds t (β alts.mapM fun alt => do pure { alt with rhs := (β update alt.rhs) })
|
| 505 |
+
| .matchExpr ref Β«metaΒ» d alts e =>
|
| 506 |
+
if alts.any fun alt => alt.vars.any fun x => ws.contains x.getId then
|
| 507 |
+
-- If a pattern variable is shadowing a variable in ws, we `pullExitPoints`
|
| 508 |
+
pullExitPoints c
|
| 509 |
+
else
|
| 510 |
+
let alts β alts.mapM fun alt => do pure { alt with rhs := (β update alt.rhs) }
|
| 511 |
+
let e β update e
|
| 512 |
+
return .matchExpr ref Β«metaΒ» d alts e
|
| 513 |
+
| .ite ref none o c t e => return .ite ref none o c (β update t) (β update e)
|
| 514 |
+
| .ite ref (some h) o cond t e =>
|
| 515 |
+
if ws.contains h.getId then
|
| 516 |
+
-- if the `h` at `if h : c then t else e` shadows a variable in `ws`, we `pullExitPoints`
|
| 517 |
+
pullExitPoints c
|
| 518 |
+
else
|
| 519 |
+
return Code.ite ref (some h) o cond (β update t) (β update e)
|
| 520 |
+
| .reassign xs stx k => return .reassign xs stx (β update k)
|
| 521 |
+
| .decl xs stx k => do
|
| 522 |
+
if xs.any fun x => ws.contains x.getId then
|
| 523 |
+
-- One the declared variables is shadowing a variable in `ws`
|
| 524 |
+
pullExitPoints c
|
| 525 |
+
else
|
| 526 |
+
return .decl xs stx (β update k)
|
| 527 |
+
| c => return c
|
| 528 |
+
update c
|
| 529 |
+
|
| 530 |
+
/--
|
| 531 |
+
Extend the set of updated variables. It assumes `ws` is a super set of `c.uvars`.
|
| 532 |
+
We **cannot** simply update the field `c.uvars`, because `c` may have shadowed some variable in `ws`.
|
| 533 |
+
See discussion at `pullExitPoints`.
|
| 534 |
+
-/
|
| 535 |
+
partial def extendUpdatedVars (c : CodeBlock) (ws : VarSet) : TermElabM CodeBlock := do
|
| 536 |
+
if ws.any fun x _ => !c.uvars.contains x then
|
| 537 |
+
-- `ws` contains a variable that is not in `c.uvars`, but in `c.dvars` (i.e., it has been shadowed)
|
| 538 |
+
pure { code := (β extendUpdatedVarsAux c.code ws), uvars := ws }
|
| 539 |
+
else
|
| 540 |
+
pure { c with uvars := ws }
|
| 541 |
+
|
| 542 |
+
private def union (sβ sβ : VarSet) : VarSet :=
|
| 543 |
+
sβ.fold (Β·.insert Β·) sβ
|
| 544 |
+
|
| 545 |
+
/--
|
| 546 |
+
Given two code blocks `cβ` and `cβ`, make sure they have the same set of updated variables.
|
| 547 |
+
Let `ws` the union of the updated variables in `cββ΅ and β΅cβ`.
|
| 548 |
+
We use `extendUpdatedVars cβ ws` and `extendUpdatedVars cβ ws`
|
| 549 |
+
-/
|
| 550 |
+
def homogenize (cβ cβ : CodeBlock) : TermElabM (CodeBlock Γ CodeBlock) := do
|
| 551 |
+
let ws := union cβ.uvars cβ.uvars
|
| 552 |
+
let cβ β extendUpdatedVars cβ ws
|
| 553 |
+
let cβ β extendUpdatedVars cβ ws
|
| 554 |
+
pure (cβ, cβ)
|
| 555 |
+
|
| 556 |
+
/--
|
| 557 |
+
Extending code blocks with variable declarations: `let x : t := v` and `let x : t β v`.
|
| 558 |
+
We remove `x` from the collection of updated variables.
|
| 559 |
+
Remark: `stx` is the syntax for the declaration (e.g., `letDecl`), and `xs` are the variables
|
| 560 |
+
declared by it. It is an array because we have let-declarations that declare multiple variables.
|
| 561 |
+
Example: `let (x, y) := t`
|
| 562 |
+
-/
|
| 563 |
+
def mkVarDeclCore (xs : Array Var) (stx : Syntax) (c : CodeBlock) : CodeBlock := {
|
| 564 |
+
code := Code.decl xs stx c.code,
|
| 565 |
+
uvars := eraseVars c.uvars xs
|
| 566 |
+
}
|
| 567 |
+
|
| 568 |
+
/--
|
| 569 |
+
Extending code blocks with reassignments: `x : t := v` and `x : t β v`.
|
| 570 |
+
Remark: `stx` is the syntax for the declaration (e.g., `letDecl`), and `xs` are the variables
|
| 571 |
+
declared by it. It is an array because we have let-declarations that declare multiple variables.
|
| 572 |
+
Example: `(x, y) β t`
|
| 573 |
+
-/
|
| 574 |
+
def mkReassignCore (xs : Array Var) (stx : Syntax) (c : CodeBlock) : TermElabM CodeBlock := do
|
| 575 |
+
let us := c.uvars
|
| 576 |
+
let ws := insertVars us xs
|
| 577 |
+
-- If `xs` contains a new updated variable, then we must use `extendUpdatedVars`.
|
| 578 |
+
-- See discussion at `pullExitPoints`
|
| 579 |
+
let code β if xs.any fun x => !us.contains x.getId then extendUpdatedVarsAux c.code ws else pure c.code
|
| 580 |
+
pure { code := .reassign xs stx code, uvars := ws }
|
| 581 |
+
|
| 582 |
+
def mkSeq (action : Syntax) (c : CodeBlock) : CodeBlock :=
|
| 583 |
+
{ c with code := .seq action c.code }
|
| 584 |
+
|
| 585 |
+
def mkTerminalAction (action : Syntax) : CodeBlock :=
|
| 586 |
+
{ code := .action action }
|
| 587 |
+
|
| 588 |
+
def mkReturn (ref : Syntax) (val : Syntax) : CodeBlock :=
|
| 589 |
+
{ code := .return ref val }
|
| 590 |
+
|
| 591 |
+
def mkBreak (ref : Syntax) : CodeBlock :=
|
| 592 |
+
{ code := .break ref }
|
| 593 |
+
|
| 594 |
+
def mkContinue (ref : Syntax) : CodeBlock :=
|
| 595 |
+
{ code := .continue ref }
|
| 596 |
+
|
| 597 |
+
def mkIte (ref : Syntax) (optIdent : Syntax) (cond : Syntax) (thenBranch : CodeBlock) (elseBranch : CodeBlock) : TermElabM CodeBlock := do
|
| 598 |
+
let x? := optIdent.getOptional?
|
| 599 |
+
let (thenBranch, elseBranch) β homogenize thenBranch elseBranch
|
| 600 |
+
return {
|
| 601 |
+
code := .ite ref x? optIdent cond thenBranch.code elseBranch.code,
|
| 602 |
+
uvars := thenBranch.uvars,
|
| 603 |
+
}
|
| 604 |
+
|
| 605 |
+
private def mkUnit : MacroM Syntax :=
|
| 606 |
+
``((β¨β© : PUnit))
|
| 607 |
+
|
| 608 |
+
private def mkPureUnit : MacroM Syntax :=
|
| 609 |
+
``(pure PUnit.unit)
|
| 610 |
+
|
| 611 |
+
def mkPureUnitAction : MacroM CodeBlock := do
|
| 612 |
+
return mkTerminalAction (β mkPureUnit)
|
| 613 |
+
|
| 614 |
+
def mkUnless (cond : Syntax) (c : CodeBlock) : MacroM CodeBlock := do
|
| 615 |
+
let thenBranch β mkPureUnitAction
|
| 616 |
+
return { c with code := .ite (β getRef) none mkNullNode cond thenBranch.code c.code }
|
| 617 |
+
|
| 618 |
+
def mkMatch (ref : Syntax) (genParam : Syntax) (discrs : Syntax) (optMotive : Syntax) (alts : Array (Alt CodeBlock)) : TermElabM CodeBlock := do
|
| 619 |
+
-- nary version of homogenize
|
| 620 |
+
let ws := alts.foldl (union Β· Β·.rhs.uvars) {}
|
| 621 |
+
let alts β alts.mapM fun alt => do
|
| 622 |
+
let rhs β extendUpdatedVars alt.rhs ws
|
| 623 |
+
return { ref := alt.ref, vars := alt.vars, patterns := alt.patterns, rhs := rhs.code : Alt Code }
|
| 624 |
+
return { code := .match ref genParam discrs optMotive alts, uvars := ws }
|
| 625 |
+
|
| 626 |
+
def mkMatchExpr (ref : Syntax) (Β«metaΒ» : Bool) (discr : Syntax) (alts : Array (AltExpr CodeBlock)) (elseBranch : CodeBlock) : TermElabM CodeBlock := do
|
| 627 |
+
-- nary version of homogenize
|
| 628 |
+
let ws := alts.foldl (union Β· Β·.rhs.uvars) {}
|
| 629 |
+
let ws := union ws elseBranch.uvars
|
| 630 |
+
let alts β alts.mapM fun alt => do
|
| 631 |
+
let rhs β extendUpdatedVars alt.rhs ws
|
| 632 |
+
return { alt with rhs := rhs.code : AltExpr Code }
|
| 633 |
+
let elseBranch β extendUpdatedVars elseBranch ws
|
| 634 |
+
return { code := .matchExpr ref Β«metaΒ» discr alts elseBranch.code, uvars := ws }
|
| 635 |
+
|
| 636 |
+
/-- Return a code block that executes `terminal` and then `k` with the value produced by `terminal`.
|
| 637 |
+
This method assumes `terminal` is a terminal -/
|
| 638 |
+
def concat (terminal : CodeBlock) (kRef : Syntax) (y? : Option Var) (k : CodeBlock) : TermElabM CodeBlock := do
|
| 639 |
+
unless hasTerminalAction terminal.code do
|
| 640 |
+
throwErrorAt kRef "`do` element is unreachable"
|
| 641 |
+
let (terminal, k) β homogenize terminal k
|
| 642 |
+
let xs := varSetToArray k.uvars
|
| 643 |
+
let y β match y? with | some y => pure y | none => `(y)
|
| 644 |
+
let ps := xs.map fun x => (x, true)
|
| 645 |
+
let ps := ps.push (y, false)
|
| 646 |
+
let jpDecl β mkFreshJP ps k.code
|
| 647 |
+
let jp := jpDecl.name
|
| 648 |
+
let terminal β liftMacroM <| convertTerminalActionIntoJmp terminal.code jp xs
|
| 649 |
+
return { code := attachJP jpDecl terminal, uvars := k.uvars }
|
| 650 |
+
|
| 651 |
+
def getLetIdVars (letId : Syntax) : Array Var :=
|
| 652 |
+
assert! letId.isOfKind ``Parser.Term.letId
|
| 653 |
+
-- def letId := leading_parser binderIdent <|> hygieneInfo
|
| 654 |
+
if letId[0].isIdent then
|
| 655 |
+
#[letId[0]]
|
| 656 |
+
else if letId[0].isOfKind hygieneInfoKind then
|
| 657 |
+
#[HygieneInfo.mkIdent letId[0] `this (canonical := true)]
|
| 658 |
+
else
|
| 659 |
+
#[]
|
| 660 |
+
|
| 661 |
+
def getLetIdDeclVars (letIdDecl : Syntax) : Array Var :=
|
| 662 |
+
assert! letIdDecl.isOfKind ``Parser.Term.letIdDecl
|
| 663 |
+
-- def letIdLhs : Parser := letId >> many (ppSpace >> letIdBinder) >> optType
|
| 664 |
+
-- def letIdDecl := leading_parser letIdLhs >> " := " >> termParser
|
| 665 |
+
getLetIdVars letIdDecl[0]
|
| 666 |
+
|
| 667 |
+
-- support both regular and syntax match
|
| 668 |
+
def getPatternVarsEx (pattern : Syntax) : TermElabM (Array Var) :=
|
| 669 |
+
getPatternVars pattern <|>
|
| 670 |
+
Quotation.getPatternVars pattern
|
| 671 |
+
|
| 672 |
+
def getPatternsVarsEx (patterns : Array Syntax) : TermElabM (Array Var) :=
|
| 673 |
+
getPatternsVars patterns <|>
|
| 674 |
+
Quotation.getPatternsVars patterns
|
| 675 |
+
|
| 676 |
+
def getLetPatDeclVars (letPatDecl : Syntax) : TermElabM (Array Var) := do
|
| 677 |
+
-- def letPatDecl := leading_parser termParser >> pushNone >> optType >> " := " >> termParser
|
| 678 |
+
let pattern := letPatDecl[0]
|
| 679 |
+
getPatternVarsEx pattern
|
| 680 |
+
|
| 681 |
+
def getLetEqnsDeclVars (letEqnsDecl : Syntax) : Array Var :=
|
| 682 |
+
assert! letEqnsDecl.isOfKind ``Parser.Term.letEqnsDecl
|
| 683 |
+
-- def letIdLhs : Parser := letId >> many (ppSpace >> letIdBinder) >> optType
|
| 684 |
+
-- def letEqnsDecl := leading_parser letIdLhs >> matchAlts
|
| 685 |
+
getLetIdVars letEqnsDecl[0]
|
| 686 |
+
|
| 687 |
+
def getLetDeclVars (letDecl : Syntax) : TermElabM (Array Var) := do
|
| 688 |
+
-- def letDecl := leading_parser letIdDecl <|> letPatDecl <|> letEqnsDecl
|
| 689 |
+
let arg := letDecl[0]
|
| 690 |
+
if arg.getKind == ``Parser.Term.letIdDecl then
|
| 691 |
+
return getLetIdDeclVars arg
|
| 692 |
+
else if arg.getKind == ``Parser.Term.letPatDecl then
|
| 693 |
+
getLetPatDeclVars arg
|
| 694 |
+
else if arg.getKind == ``Parser.Term.letEqnsDecl then
|
| 695 |
+
return getLetEqnsDeclVars arg
|
| 696 |
+
else
|
| 697 |
+
throwError "unexpected kind of let declaration"
|
| 698 |
+
|
| 699 |
+
def getDoLetVars (doLet : Syntax) : TermElabM (Array Var) :=
|
| 700 |
+
-- leading_parser "let " >> optional "mut " >> letDecl
|
| 701 |
+
getLetDeclVars doLet[2]
|
| 702 |
+
|
| 703 |
+
def getDoHaveVars (doHave : Syntax) : TermElabM (Array Var) :=
|
| 704 |
+
-- leading_parser "have" >> letDecl
|
| 705 |
+
getLetDeclVars doHave[1]
|
| 706 |
+
|
| 707 |
+
def getDoLetRecVars (doLetRec : Syntax) : TermElabM (Array Var) := do
|
| 708 |
+
-- letRecDecls is an array of `(group (optional attributes >> letDecl))`
|
| 709 |
+
let letRecDecls := doLetRec[1][0].getSepArgs
|
| 710 |
+
let letDecls := letRecDecls.map fun p => p[2]
|
| 711 |
+
let mut allVars := #[]
|
| 712 |
+
for letDecl in letDecls do
|
| 713 |
+
let vars β getLetDeclVars letDecl
|
| 714 |
+
allVars := allVars ++ vars
|
| 715 |
+
return allVars
|
| 716 |
+
|
| 717 |
+
-- ident >> optType >> leftArrow >> termParser
|
| 718 |
+
def getDoIdDeclVar (doIdDecl : Syntax) : Var :=
|
| 719 |
+
doIdDecl[0]
|
| 720 |
+
|
| 721 |
+
-- termParser >> leftArrow >> termParser >> optional (" | " >> termParser)
|
| 722 |
+
def getDoPatDeclVars (doPatDecl : Syntax) : TermElabM (Array Var) := do
|
| 723 |
+
let pattern := doPatDecl[0]
|
| 724 |
+
getPatternVarsEx pattern
|
| 725 |
+
|
| 726 |
+
-- leading_parser "let " >> optional "mut " >> (doIdDecl <|> doPatDecl)
|
| 727 |
+
def getDoLetArrowVars (doLetArrow : Syntax) : TermElabM (Array Var) := do
|
| 728 |
+
let decl := doLetArrow[2]
|
| 729 |
+
if decl.getKind == ``Parser.Term.doIdDecl then
|
| 730 |
+
return #[getDoIdDeclVar decl]
|
| 731 |
+
else if decl.getKind == ``Parser.Term.doPatDecl then
|
| 732 |
+
getDoPatDeclVars decl
|
| 733 |
+
else
|
| 734 |
+
throwError "unexpected kind of `do` declaration"
|
| 735 |
+
|
| 736 |
+
def getDoReassignVars (doReassign : Syntax) : TermElabM (Array Var) := do
|
| 737 |
+
let arg := doReassign[0]
|
| 738 |
+
if arg.getKind == ``Parser.Term.letIdDecl then
|
| 739 |
+
return getLetIdDeclVars arg
|
| 740 |
+
else if arg.getKind == ``Parser.Term.letPatDecl then
|
| 741 |
+
getLetPatDeclVars arg
|
| 742 |
+
else
|
| 743 |
+
throwError "unexpected kind of reassignment"
|
| 744 |
+
|
| 745 |
+
def mkDoSeq (doElems : Array Syntax) : Syntax :=
|
| 746 |
+
mkNode `Lean.Parser.Term.doSeqIndent #[mkNullNode <| doElems.map fun doElem => mkNullNode #[doElem, mkNullNode]]
|
| 747 |
+
|
| 748 |
+
/--
|
| 749 |
+
If the given syntax is a `doIf`, return an equivalent `doIf` that has an `else` but no `else if`s or `if let`s. -/
|
| 750 |
+
private def expandDoIf? (stx : Syntax) : MacroM (Option Syntax) := match stx with
|
| 751 |
+
| `(doElem|if $_:doIfProp then $_ else $_) => pure none
|
| 752 |
+
| `(doElem|if $cond:doIfCond then $t $[else if $conds:doIfCond then $ts]* $[else $e?]?) => withRef stx do
|
| 753 |
+
let mut e := e?.getD (β `(doSeq|pure PUnit.unit))
|
| 754 |
+
let mut eIsSeq := true
|
| 755 |
+
for (cond, t) in Array.zip (conds.reverse.push cond) (ts.reverse.push t) do
|
| 756 |
+
e β if eIsSeq then pure e else `(doSeq|$e:doElem)
|
| 757 |
+
e β match cond with
|
| 758 |
+
| `(doIfCond|let $pat := $d) => `(doElem| match $d:term with | $pat:term => $t | _ => $e)
|
| 759 |
+
| `(doIfCond|let $pat β $d) => `(doElem| match β $d with | $pat:term => $t | _ => $e)
|
| 760 |
+
| `(doIfCond|$cond:doIfProp) => `(doElem| if $cond:doIfProp then $t else $e)
|
| 761 |
+
| _ => `(doElem| if $(Syntax.missing) then $t else $e)
|
| 762 |
+
eIsSeq := false
|
| 763 |
+
return some e
|
| 764 |
+
| _ => pure none
|
| 765 |
+
|
| 766 |
+
/--
|
| 767 |
+
If the given syntax is a `doLetExpr` or `doLetMetaExpr`, return an equivalent `doIf` that has an `else` but no `else if`s or `if let`s. -/
|
| 768 |
+
private def expandDoLetExpr? (stx : Syntax) (doElems : List Syntax) : MacroM (Option Syntax) := match stx with
|
| 769 |
+
| `(doElem| let_expr $pat:matchExprPat := $discr:term | $elseBranch:doSeq) =>
|
| 770 |
+
return some (β `(doElem| match_expr (meta := false) $discr:term with
|
| 771 |
+
| $pat:matchExprPat => $(mkDoSeq doElems.toArray)
|
| 772 |
+
| _ => $elseBranch))
|
| 773 |
+
| `(doElem| let_expr $pat:matchExprPat β $discr:term | $elseBranch:doSeq) =>
|
| 774 |
+
return some (β `(doElem| match_expr $discr:term with
|
| 775 |
+
| $pat:matchExprPat => $(mkDoSeq doElems.toArray)
|
| 776 |
+
| _ => $elseBranch))
|
| 777 |
+
| _ => return none
|
| 778 |
+
|
| 779 |
+
structure DoIfView where
|
| 780 |
+
ref : Syntax
|
| 781 |
+
optIdent : Syntax
|
| 782 |
+
cond : Syntax
|
| 783 |
+
thenBranch : Syntax
|
| 784 |
+
elseBranch : Syntax
|
| 785 |
+
|
| 786 |
+
/-- This method assumes `expandDoIf?` is not applicable. -/
|
| 787 |
+
private def mkDoIfView (doIf : Syntax) : DoIfView := {
|
| 788 |
+
ref := doIf
|
| 789 |
+
optIdent := doIf[1][0]
|
| 790 |
+
cond := doIf[1][1]
|
| 791 |
+
thenBranch := doIf[3]
|
| 792 |
+
elseBranch := doIf[5][1]
|
| 793 |
+
}
|
| 794 |
+
|
| 795 |
+
/--
|
| 796 |
+
We use `MProd` instead of `Prod` to group values when expanding the
|
| 797 |
+
`do` notation. `MProd` is a universe monomorphic product.
|
| 798 |
+
The motivation is to generate simpler universe constraints in code
|
| 799 |
+
that was not written by the user.
|
| 800 |
+
Note that we are not restricting the macro power since the
|
| 801 |
+
`Bind.bind` combinator already forces values computed by monadic
|
| 802 |
+
actions to be in the same universe.
|
| 803 |
+
-/
|
| 804 |
+
private def mkTuple (elems : Array Syntax) : MacroM Syntax := do
|
| 805 |
+
if elems.size = 0 then
|
| 806 |
+
mkUnit
|
| 807 |
+
else if h : elems.size = 1 then
|
| 808 |
+
return elems[0]
|
| 809 |
+
else
|
| 810 |
+
elems.extract 0 (elems.size - 1) |>.foldrM (init := elems.back!) fun elem tuple =>
|
| 811 |
+
``(MProd.mk $elem $tuple)
|
| 812 |
+
|
| 813 |
+
/-- Return `some action` if `doElem` is a `doExpr <action>`-/
|
| 814 |
+
def isDoExpr? (doElem : Syntax) : Option Syntax :=
|
| 815 |
+
if doElem.getKind == ``Parser.Term.doExpr then
|
| 816 |
+
some doElem[0]
|
| 817 |
+
else
|
| 818 |
+
none
|
| 819 |
+
|
| 820 |
+
/--
|
| 821 |
+
Given `uvars := #[a_1, ..., a_n, a_{n+1}]` construct term
|
| 822 |
+
```
|
| 823 |
+
let a_1 := x.1
|
| 824 |
+
let x := x.2
|
| 825 |
+
let a_2 := x.1
|
| 826 |
+
let x := x.2
|
| 827 |
+
...
|
| 828 |
+
let a_n := x.1
|
| 829 |
+
let a_{n+1} := x.2
|
| 830 |
+
body
|
| 831 |
+
```
|
| 832 |
+
Special cases
|
| 833 |
+
- `uvars := #[]` => `body`
|
| 834 |
+
- `uvars := #[a]` => `let a := x; body`
|
| 835 |
+
|
| 836 |
+
|
| 837 |
+
We use this method when expanding the `for-in` notation.
|
| 838 |
+
-/
|
| 839 |
+
private def destructTuple (uvars : Array Var) (x : Syntax) (body : Syntax) : MacroM Syntax := do
|
| 840 |
+
if uvars.size = 0 then
|
| 841 |
+
return body
|
| 842 |
+
else if h : uvars.size = 1 then
|
| 843 |
+
`(let $(uvars[0]):ident := $x; $body)
|
| 844 |
+
else
|
| 845 |
+
destruct uvars.toList x body
|
| 846 |
+
where
|
| 847 |
+
destruct (as : List Var) (x : Syntax) (body : Syntax) : MacroM Syntax := do
|
| 848 |
+
match as with
|
| 849 |
+
| [a, b] => `(let $a:ident := $x.1; let $b:ident := $x.2; $body)
|
| 850 |
+
| a :: as => withFreshMacroScope do
|
| 851 |
+
let rest β destruct as (β `(x)) body
|
| 852 |
+
`(let $a:ident := $x.1; let x := $x.2; $rest)
|
| 853 |
+
| _ => unreachable!
|
| 854 |
+
|
| 855 |
+
/-!
|
| 856 |
+
The procedure `ToTerm.run` converts a `CodeBlock` into a `Syntax` term.
|
| 857 |
+
We use this method to convert
|
| 858 |
+
1- The `CodeBlock` for a root `do ...` term into a `Syntax` term. This kind of
|
| 859 |
+
`CodeBlock` never contains `break` nor `continue`. Moreover, the collection
|
| 860 |
+
of updated variables is not packed into the result.
|
| 861 |
+
Thus, we have two kinds of exit points
|
| 862 |
+
- `Code.action e` which is converted into `e`
|
| 863 |
+
- `Code.return _ e` which is converted into `pure e`
|
| 864 |
+
|
| 865 |
+
We use `Kind.regular` for this case.
|
| 866 |
+
|
| 867 |
+
2- The `CodeBlock` for `b` at `for x in xs do b`. In this case, we need to generate
|
| 868 |
+
a `Syntax` term representing a function for the `xs.forIn` combinator.
|
| 869 |
+
|
| 870 |
+
a) If `b` contain a `Code.return _ a` exit point. The generated `Syntax` term
|
| 871 |
+
has type `m (ForInStep (Option Ξ± Γ Ο))`, where `a : Ξ±`, and the `Ο` is the type
|
| 872 |
+
of the tuple of variables reassigned by `b`.
|
| 873 |
+
We use `Kind.forInWithReturn` for this case
|
| 874 |
+
|
| 875 |
+
b) If `b` does not contain a `Code.return _ a` exit point. Then, the generated
|
| 876 |
+
`Syntax` term has type `m (ForInStep Ο)`.
|
| 877 |
+
We use `Kind.forIn` for this case.
|
| 878 |
+
|
| 879 |
+
3- The `CodeBlock` `c` for a `do` sequence nested in a monadic combinator (e.g., `MonadExcept.tryCatch`).
|
| 880 |
+
|
| 881 |
+
The generated `Syntax` term for `c` must inform whether `c` "exited" using `Code.action`, `Code.return`,
|
| 882 |
+
`Code.break` or `Code.continue`. We use the auxiliary types `DoResult`s for storing this information.
|
| 883 |
+
For example, the auxiliary type `DoResultPBC Ξ± Ο` is used for a code block that exits with `Code.action`,
|
| 884 |
+
**and** `Code.break`/`Code.continue`, `Ξ±` is the type of values produced by the exit `action`, and
|
| 885 |
+
`Ο` is the type of the tuple of reassigned variables.
|
| 886 |
+
The type `DoResult Ξ± Ξ² Ο` is usedf for code blocks that exit with
|
| 887 |
+
`Code.action`, `Code.return`, **and** `Code.break`/`Code.continue`, `Ξ²` is the type of the returned values.
|
| 888 |
+
We don't use `DoResult Ξ± Ξ² Ο` for all cases because:
|
| 889 |
+
|
| 890 |
+
a) The elaborator would not be able to infer all type parameters without extra annotations. For example,
|
| 891 |
+
if the code block does not contain `Code.return _ _`, the elaborator will not be able to infer `Ξ²`.
|
| 892 |
+
|
| 893 |
+
b) We need to pattern match on the result produced by the combinator (e.g., `MonadExcept.tryCatch`),
|
| 894 |
+
but we don't want to consider "unreachable" cases.
|
| 895 |
+
|
| 896 |
+
We do not distinguish between cases that contain `break`, but not `continue`, and vice versa.
|
| 897 |
+
|
| 898 |
+
When listing all cases, we use `a` to indicate the code block contains `Code.action _`, `r` for `Code.return _ _`,
|
| 899 |
+
and `b/c` for a code block that contains `Code.break _` or `Code.continue _`.
|
| 900 |
+
|
| 901 |
+
- `a`: `Kind.regular`, type `m (Ξ± Γ Ο)`
|
| 902 |
+
|
| 903 |
+
- `r`: `Kind.regular`, type `m (Ξ± Γ Ο)`
|
| 904 |
+
Note that the code that pattern matches on the result will behave differently in this case.
|
| 905 |
+
It produces `return a` for this case, and `pure a` for the previous one.
|
| 906 |
+
|
| 907 |
+
- `b/c`: `Kind.nestedBC`, type `m (DoResultBC Ο)`
|
| 908 |
+
|
| 909 |
+
- `a` and `r`: `Kind.nestedPR`, type `m (DoResultPR Ξ± Ξ² Ο)`
|
| 910 |
+
|
| 911 |
+
- `a` and `bc`: `Kind.nestedSBC`, type `m (DoResultSBC Ξ± Ο)`
|
| 912 |
+
|
| 913 |
+
- `r` and `bc`: `Kind.nestedSBC`, type `m (DoResultSBC Ξ± Ο)`
|
| 914 |
+
Again the code that pattern matches on the result will behave differently in this case and
|
| 915 |
+
the previous one. It produces `return a` for the constructor `DoResultSPR.pureReturn a u` for
|
| 916 |
+
this case, and `pure a` for the previous case.
|
| 917 |
+
|
| 918 |
+
- `a`, `r`, `b/c`: `Kind.nestedPRBC`, type type `m (DoResultPRBC Ξ± Ξ² Ο)`
|
| 919 |
+
|
| 920 |
+
Here is the recipe for adding new combinators with nested `do`s.
|
| 921 |
+
Example: suppose we want to support `repeat doSeq`. Assuming we have `repeat : m Ξ± β m Ξ±`
|
| 922 |
+
1- Convert `doSeq` into `codeBlock : CodeBlock`
|
| 923 |
+
2- Create term `term` using `mkNestedTerm code m uvars a r bc` where
|
| 924 |
+
`code` is `codeBlock.code`, `uvars` is an array containing `codeBlock.uvars`,
|
| 925 |
+
`m` is a `Syntax` representing the Monad, and
|
| 926 |
+
`a` is true if `code` contains `Code.action _`,
|
| 927 |
+
`r` is true if `code` contains `Code.return _ _`,
|
| 928 |
+
`bc` is true if `code` contains `Code.break _` or `Code.continue _`.
|
| 929 |
+
|
| 930 |
+
Remark: for combinators such as `repeat` that take a single `doSeq`, all
|
| 931 |
+
arguments, but `m`, are extracted from `codeBlock`.
|
| 932 |
+
3- Create the term `repeat $term`
|
| 933 |
+
4- and then, convert it into a `doSeq` using `matchNestedTermResult ref (repeat $term) uvsar a r bc`
|
| 934 |
+
|
| 935 |
+
-/
|
| 936 |
+
|
| 937 |
+
/--
|
| 938 |
+
Helper method for annotating `term` with the raw syntax `ref`.
|
| 939 |
+
We use this method to implement finer-grained term infos for `do`-blocks.
|
| 940 |
+
|
| 941 |
+
We use `withRef term` to make sure the synthetic position for the `with_annotate_term` is equal
|
| 942 |
+
to the one for `term`. This is important for producing error messages when there is a type mismatch.
|
| 943 |
+
Consider the following example:
|
| 944 |
+
```
|
| 945 |
+
opaque f : IO Nat
|
| 946 |
+
|
| 947 |
+
def g : IO String := do
|
| 948 |
+
f
|
| 949 |
+
```
|
| 950 |
+
There is at type mismatch at `f`, but it is detected when elaborating the expanded term
|
| 951 |
+
containing the `with_annotate_term .. f`. The current `getRef` when this `annotate` is invoked
|
| 952 |
+
is not necessarily `f`. Actually, it is the whole `do`-block. By using `withRef` we ensure
|
| 953 |
+
the synthetic position for the `with_annotate_term ..` is equal to `term`.
|
| 954 |
+
Recall that synthetic positions are used when generating error messages.
|
| 955 |
+
-/
|
| 956 |
+
def annotate [Monad m] [MonadRef m] [MonadQuotation m] (ref : Syntax) (term : Syntax) : m Syntax :=
|
| 957 |
+
withRef term <| `(with_annotate_term $ref $term)
|
| 958 |
+
|
| 959 |
+
namespace ToTerm
|
| 960 |
+
|
| 961 |
+
inductive Kind where
|
| 962 |
+
| regular
|
| 963 |
+
| forIn
|
| 964 |
+
| forInWithReturn
|
| 965 |
+
| nestedBC
|
| 966 |
+
| nestedPR
|
| 967 |
+
| nestedSBC
|
| 968 |
+
| nestedPRBC
|
| 969 |
+
|
| 970 |
+
instance : Inhabited Kind := β¨Kind.regularβ©
|
| 971 |
+
|
| 972 |
+
def Kind.isRegular : Kind β Bool
|
| 973 |
+
| .regular => true
|
| 974 |
+
| _ => false
|
| 975 |
+
|
| 976 |
+
structure Context where
|
| 977 |
+
/-- Syntax to reference the monad associated with the do notation. -/
|
| 978 |
+
m : Syntax
|
| 979 |
+
/-- Syntax to reference the result of the monadic computation performed by the do notation. -/
|
| 980 |
+
returnType : Syntax
|
| 981 |
+
uvars : Array Var
|
| 982 |
+
kind : Kind
|
| 983 |
+
|
| 984 |
+
abbrev M := ReaderT Context MacroM
|
| 985 |
+
|
| 986 |
+
def mkUVarTuple : M Syntax := do
|
| 987 |
+
let ctx β read
|
| 988 |
+
mkTuple ctx.uvars
|
| 989 |
+
|
| 990 |
+
def returnToTerm (val : Syntax) : M Syntax := do
|
| 991 |
+
let ctx β read
|
| 992 |
+
let u β mkUVarTuple
|
| 993 |
+
match ctx.kind with
|
| 994 |
+
| .regular => if ctx.uvars.isEmpty then ``(Pure.pure $val) else ``(Pure.pure (MProd.mk $val $u))
|
| 995 |
+
| .forIn => ``(Pure.pure (ForInStep.done $u))
|
| 996 |
+
| .forInWithReturn => ``(Pure.pure (ForInStep.done (MProd.mk (some $val) $u)))
|
| 997 |
+
| .nestedBC => unreachable!
|
| 998 |
+
| .nestedPR => ``(Pure.pure (DoResultPR.Β«returnΒ» $val $u))
|
| 999 |
+
| .nestedSBC => ``(Pure.pure (DoResultSBC.Β«pureReturnΒ» $val $u))
|
| 1000 |
+
| .nestedPRBC => ``(Pure.pure (DoResultPRBC.Β«returnΒ» $val $u))
|
| 1001 |
+
|
| 1002 |
+
def continueToTerm : M Syntax := do
|
| 1003 |
+
let ctx β read
|
| 1004 |
+
let u β mkUVarTuple
|
| 1005 |
+
match ctx.kind with
|
| 1006 |
+
| .regular => unreachable!
|
| 1007 |
+
| .forIn => ``(Pure.pure (ForInStep.yield $u))
|
| 1008 |
+
| .forInWithReturn => ``(Pure.pure (ForInStep.yield (MProd.mk none $u)))
|
| 1009 |
+
| .nestedBC => ``(Pure.pure (DoResultBC.Β«continueΒ» $u))
|
| 1010 |
+
| .nestedPR => unreachable!
|
| 1011 |
+
| .nestedSBC => ``(Pure.pure (DoResultSBC.Β«continueΒ» $u))
|
| 1012 |
+
| .nestedPRBC => ``(Pure.pure (DoResultPRBC.Β«continueΒ» $u))
|
| 1013 |
+
|
| 1014 |
+
def breakToTerm : M Syntax := do
|
| 1015 |
+
let ctx β read
|
| 1016 |
+
let u β mkUVarTuple
|
| 1017 |
+
match ctx.kind with
|
| 1018 |
+
| .regular => unreachable!
|
| 1019 |
+
| .forIn => ``(Pure.pure (ForInStep.done $u))
|
| 1020 |
+
| .forInWithReturn => ``(Pure.pure (ForInStep.done (MProd.mk none $u)))
|
| 1021 |
+
| .nestedBC => ``(Pure.pure (DoResultBC.Β«breakΒ» $u))
|
| 1022 |
+
| .nestedPR => unreachable!
|
| 1023 |
+
| .nestedSBC => ``(Pure.pure (DoResultSBC.Β«breakΒ» $u))
|
| 1024 |
+
| .nestedPRBC => ``(Pure.pure (DoResultPRBC.Β«breakΒ» $u))
|
| 1025 |
+
|
| 1026 |
+
def actionTerminalToTerm (action : Syntax) : M Syntax := withRef action <| withFreshMacroScope do
|
| 1027 |
+
let ctx β read
|
| 1028 |
+
let u β mkUVarTuple
|
| 1029 |
+
match ctx.kind with
|
| 1030 |
+
| .regular => if ctx.uvars.isEmpty then pure action else ``(Bind.bind $action fun y => Pure.pure (MProd.mk y $u))
|
| 1031 |
+
| .forIn => ``(Bind.bind $action fun (_ : PUnit) => Pure.pure (ForInStep.yield $u))
|
| 1032 |
+
| .forInWithReturn => ``(Bind.bind $action fun (_ : PUnit) => Pure.pure (ForInStep.yield (MProd.mk none $u)))
|
| 1033 |
+
| .nestedBC => unreachable!
|
| 1034 |
+
| .nestedPR => ``(Bind.bind $action fun y => (Pure.pure (DoResultPR.Β«pureΒ» y $u)))
|
| 1035 |
+
| .nestedSBC => ``(Bind.bind $action fun y => (Pure.pure (DoResultSBC.Β«pureReturnΒ» y $u)))
|
| 1036 |
+
| .nestedPRBC => ``(Bind.bind $action fun y => (Pure.pure (DoResultPRBC.Β«pureΒ» y $u)))
|
| 1037 |
+
|
| 1038 |
+
def seqToTerm (action : Syntax) (k : Syntax) : M Syntax := withRef action <| withFreshMacroScope do
|
| 1039 |
+
if action.getKind == ``Parser.Term.doDbgTrace then
|
| 1040 |
+
let msg := action[1]
|
| 1041 |
+
`(dbg_trace $msg; $k)
|
| 1042 |
+
else if action.getKind == ``Parser.Term.doAssert then
|
| 1043 |
+
let cond := action[1]
|
| 1044 |
+
`(assert! $cond; $k)
|
| 1045 |
+
else if action.getKind == ``Parser.Term.doDebugAssert then
|
| 1046 |
+
let cond := action[1]
|
| 1047 |
+
`(debugAssert| debug_assert! $cond; $k)
|
| 1048 |
+
else
|
| 1049 |
+
let action β withRef action ``(($action : $((βread).m) PUnit))
|
| 1050 |
+
``(Bind.bind $action (fun (_ : PUnit) => $k))
|
| 1051 |
+
|
| 1052 |
+
def declToTerm (decl : Syntax) (k : Syntax) : M Syntax := withRef decl <| withFreshMacroScope do
|
| 1053 |
+
let kind := decl.getKind
|
| 1054 |
+
if kind == ``Parser.Term.doLet then
|
| 1055 |
+
let letDecl := decl[2]
|
| 1056 |
+
`(let $letDecl:letDecl; $k)
|
| 1057 |
+
else if kind == ``Parser.Term.doLetRec then
|
| 1058 |
+
let letRecToken := decl[0]
|
| 1059 |
+
let letRecDecls := decl[1]
|
| 1060 |
+
return mkNode ``Parser.Term.letrec #[letRecToken, letRecDecls, mkNullNode, k]
|
| 1061 |
+
else if kind == ``Parser.Term.doLetArrow then
|
| 1062 |
+
let arg := decl[2]
|
| 1063 |
+
if arg.getKind == ``Parser.Term.doIdDecl then
|
| 1064 |
+
let id := arg[0]
|
| 1065 |
+
let type := expandOptType id arg[1]
|
| 1066 |
+
let doElem := arg[3]
|
| 1067 |
+
-- `doElem` must be a `doExpr action`. See `doLetArrowToCode`
|
| 1068 |
+
match isDoExpr? doElem with
|
| 1069 |
+
| some action =>
|
| 1070 |
+
let action β withRef action `(($action : $((β read).m) $type))
|
| 1071 |
+
``(Bind.bind $action (fun ($id:ident : $type) => $k))
|
| 1072 |
+
| none => Macro.throwErrorAt decl "unexpected kind of `do` declaration"
|
| 1073 |
+
else
|
| 1074 |
+
Macro.throwErrorAt decl "unexpected kind of `do` declaration"
|
| 1075 |
+
else if kind == ``Parser.Term.doHave then
|
| 1076 |
+
-- The `have` term is of the form `"have " >> letDecl >> optSemicolon termParser`
|
| 1077 |
+
let args := decl.getArgs
|
| 1078 |
+
let args := args ++ #[mkNullNode /- optional ';' -/, k]
|
| 1079 |
+
return mkNode `Lean.Parser.Term.Β«haveΒ» args
|
| 1080 |
+
else
|
| 1081 |
+
Macro.throwErrorAt decl "unexpected kind of `do` declaration"
|
| 1082 |
+
|
| 1083 |
+
def reassignToTerm (reassign : Syntax) (k : Syntax) : MacroM Syntax := withRef reassign <| withFreshMacroScope do
|
| 1084 |
+
match reassign with
|
| 1085 |
+
| `(doElem| $x:ident := $rhs) => `(let $x:ident := ensure_type_of% $x $(quote "invalid reassignment, value") $rhs; $k)
|
| 1086 |
+
| `(doElem| $e:term := $rhs) => `(let $e:term := ensure_type_of% $e $(quote "invalid reassignment, value") $rhs; $k)
|
| 1087 |
+
| _ =>
|
| 1088 |
+
-- Note that `doReassignArrow` is expanded by `doReassignArrowToCode
|
| 1089 |
+
Macro.throwErrorAt reassign "unexpected kind of `do` reassignment"
|
| 1090 |
+
|
| 1091 |
+
def mkIte (optIdent : Syntax) (cond : Syntax) (thenBranch : Syntax) (elseBranch : Syntax) : MacroM Syntax := do
|
| 1092 |
+
if optIdent.isNone then
|
| 1093 |
+
``(if $cond then $thenBranch else $elseBranch)
|
| 1094 |
+
else
|
| 1095 |
+
let h := optIdent[0]
|
| 1096 |
+
``(if $h:ident : $cond then $thenBranch else $elseBranch)
|
| 1097 |
+
|
| 1098 |
+
def mkJoinPoint (j : Name) (ps : Array (Syntax Γ Bool)) (body : Syntax) (k : Syntax) : M Syntax := withRef body <| withFreshMacroScope do
|
| 1099 |
+
let pTypes β ps.mapM fun β¨id, useTypeOfβ© => do if useTypeOf then `(type_of% $id) else `(_)
|
| 1100 |
+
let ps := ps.map (Β·.1)
|
| 1101 |
+
/-
|
| 1102 |
+
We use `let_delayed` instead of `let` for joinpoints to make sure `$k` is elaborated before `$body`.
|
| 1103 |
+
By elaborating `$k` first, we "learn" more about `$body`'s type.
|
| 1104 |
+
For example, consider the following example `do` expression
|
| 1105 |
+
```
|
| 1106 |
+
def f (x : Nat) : IO Unit := do
|
| 1107 |
+
if x > 0 then
|
| 1108 |
+
IO.println "x is not zero" -- Error is here
|
| 1109 |
+
IO.mkRef true
|
| 1110 |
+
```
|
| 1111 |
+
it is expanded into
|
| 1112 |
+
```
|
| 1113 |
+
def f (x : Nat) : IO Unit := do
|
| 1114 |
+
let jp (u : Unit) : IO _ :=
|
| 1115 |
+
IO.mkRef true;
|
| 1116 |
+
if x > 0 then
|
| 1117 |
+
IO.println "not zero"
|
| 1118 |
+
jp ()
|
| 1119 |
+
else
|
| 1120 |
+
jp ()
|
| 1121 |
+
```
|
| 1122 |
+
If we use the regular `let` instead of `let_delayed`, the joinpoint `jp` will be elaborated and its type will be inferred to be `Unit β IO (IO.Ref Bool)`.
|
| 1123 |
+
Then, we get a typing error at `jp ()`. By using `let_delayed`, we first elaborate `if x > 0 ...` and learn that `jp` has type `Unit β IO Unit`.
|
| 1124 |
+
Then, we get the expected type mismatch error at `IO.mkRef true`. -/
|
| 1125 |
+
`(let_delayed $(β mkIdentFromRef j):ident $[($ps : $pTypes)]* : $((β read).m) _ := $body; $k)
|
| 1126 |
+
|
| 1127 |
+
def mkJmp (ref : Syntax) (j : Name) (args : Array Syntax) : Syntax :=
|
| 1128 |
+
Syntax.mkApp (mkIdentFrom ref j) args
|
| 1129 |
+
|
| 1130 |
+
partial def toTerm (c : Code) : M Syntax := do
|
| 1131 |
+
let term β go c
|
| 1132 |
+
if let some ref := c.getRef? then
|
| 1133 |
+
annotate ref term
|
| 1134 |
+
else
|
| 1135 |
+
return term
|
| 1136 |
+
where
|
| 1137 |
+
go (c : Code) : M Syntax := do
|
| 1138 |
+
match c with
|
| 1139 |
+
| .return ref val => withRef ref <| returnToTerm val
|
| 1140 |
+
| .continue ref => withRef ref continueToTerm
|
| 1141 |
+
| .break ref => withRef ref breakToTerm
|
| 1142 |
+
| .action e => actionTerminalToTerm e
|
| 1143 |
+
| .joinpoint j ps b k => mkJoinPoint j ps (β toTerm b) (β toTerm k)
|
| 1144 |
+
| .jmp ref j args => return mkJmp ref j args
|
| 1145 |
+
| .decl _ stx k => declToTerm stx (β toTerm k)
|
| 1146 |
+
| .reassign _ stx k => reassignToTerm stx (β toTerm k)
|
| 1147 |
+
| .seq stx k => seqToTerm stx (β toTerm k)
|
| 1148 |
+
| .ite ref _ o c t e => withRef ref <| do mkIte o c (β toTerm t) (β toTerm e)
|
| 1149 |
+
| .match ref genParam discrs optMotive alts =>
|
| 1150 |
+
let mut termAlts := #[]
|
| 1151 |
+
for alt in alts do
|
| 1152 |
+
let rhs β toTerm alt.rhs
|
| 1153 |
+
let termAlt := mkNode ``Parser.Term.matchAlt #[mkAtomFrom alt.ref "|", mkNullNode #[alt.patterns], mkAtomFrom alt.ref "=>", rhs]
|
| 1154 |
+
termAlts := termAlts.push termAlt
|
| 1155 |
+
let termMatchAlts := mkNode ``Parser.Term.matchAlts #[mkNullNode termAlts]
|
| 1156 |
+
return mkNode ``Parser.Term.Β«matchΒ» #[mkAtomFrom ref "match", genParam, optMotive, discrs, mkAtomFrom ref "with", termMatchAlts]
|
| 1157 |
+
| .matchExpr ref Β«metaΒ» d alts elseBranch => withFreshMacroScope do
|
| 1158 |
+
let d' β `(discr)
|
| 1159 |
+
let mut termAlts := #[]
|
| 1160 |
+
for alt in alts do
|
| 1161 |
+
let rhs β `(($(β toTerm alt.rhs) : $((β read).m) _))
|
| 1162 |
+
let optVar := if let some var := alt.var? then mkNullNode #[var, mkAtomFrom var "@"] else mkNullNode #[]
|
| 1163 |
+
let pat := mkNode ``Parser.Term.matchExprPat #[optVar, alt.funName, mkNullNode alt.pvars]
|
| 1164 |
+
let termAlt := mkNode ``Parser.Term.matchExprAlt #[mkAtomFrom alt.ref "|", pat, mkAtomFrom alt.ref "=>", rhs]
|
| 1165 |
+
termAlts := termAlts.push termAlt
|
| 1166 |
+
let elseBranch := mkNode ``Parser.Term.matchExprElseAlt #[mkAtomFrom ref "|", mkHole ref, mkAtomFrom ref "=>", (β toTerm elseBranch)]
|
| 1167 |
+
let termMatchExprAlts := mkNode ``Parser.Term.matchExprAlts #[mkNullNode termAlts, elseBranch]
|
| 1168 |
+
let body := mkNode ``Parser.Term.matchExpr #[mkAtomFrom ref "match_expr", d', mkAtomFrom ref "with", termMatchExprAlts]
|
| 1169 |
+
if Β«metaΒ» then
|
| 1170 |
+
`(Bind.bind (instantiateMVarsIfMVarApp $d) fun discr => $body)
|
| 1171 |
+
else
|
| 1172 |
+
`(let discr := $d; $body)
|
| 1173 |
+
|
| 1174 |
+
def run (code : Code) (m : Syntax) (returnType : Syntax) (uvars : Array Var := #[]) (kind := Kind.regular) : MacroM Syntax :=
|
| 1175 |
+
toTerm code { m, returnType, kind, uvars }
|
| 1176 |
+
|
| 1177 |
+
/-- Given
|
| 1178 |
+
- `a` is true if the code block has a `Code.action _` exit point
|
| 1179 |
+
- `r` is true if the code block has a `Code.return _ _` exit point
|
| 1180 |
+
- `bc` is true if the code block has a `Code.break _` or `Code.continue _` exit point
|
| 1181 |
+
|
| 1182 |
+
generate Kind. See comment at the beginning of the `ToTerm` namespace. -/
|
| 1183 |
+
def mkNestedKind (a r bc : Bool) : Kind :=
|
| 1184 |
+
match a, r, bc with
|
| 1185 |
+
| true, false, false => .regular
|
| 1186 |
+
| false, true, false => .regular
|
| 1187 |
+
| false, false, true => .nestedBC
|
| 1188 |
+
| true, true, false => .nestedPR
|
| 1189 |
+
| true, false, true => .nestedSBC
|
| 1190 |
+
| false, true, true => .nestedSBC
|
| 1191 |
+
| true, true, true => .nestedPRBC
|
| 1192 |
+
| false, false, false => unreachable!
|
| 1193 |
+
|
| 1194 |
+
def mkNestedTerm (code : Code) (m : Syntax) (returnType : Syntax) (uvars : Array Var) (a r bc : Bool) : MacroM Syntax := do
|
| 1195 |
+
ToTerm.run code m returnType uvars (mkNestedKind a r bc)
|
| 1196 |
+
|
| 1197 |
+
/-- Given a term `term` produced by `ToTerm.run`, pattern match on its result.
|
| 1198 |
+
See comment at the beginning of the `ToTerm` namespace.
|
| 1199 |
+
|
| 1200 |
+
- `a` is true if the code block has a `Code.action _` exit point
|
| 1201 |
+
- `r` is true if the code block has a `Code.return _ _` exit point
|
| 1202 |
+
- `bc` is true if the code block has a `Code.break _` or `Code.continue _` exit point
|
| 1203 |
+
|
| 1204 |
+
The result is a sequence of `doElem` -/
|
| 1205 |
+
def matchNestedTermResult (term : Syntax) (uvars : Array Var) (a r bc : Bool) : MacroM (List Syntax) := do
|
| 1206 |
+
let toDoElems (auxDo : Syntax) : List Syntax := getDoSeqElems (getDoSeq auxDo)
|
| 1207 |
+
let u β mkTuple uvars
|
| 1208 |
+
match a, r, bc with
|
| 1209 |
+
| true, false, false =>
|
| 1210 |
+
if uvars.isEmpty then
|
| 1211 |
+
return toDoElems (β `(do $term:term))
|
| 1212 |
+
else
|
| 1213 |
+
return toDoElems (β `(do let r β $term:term; $u:term := r.2; pure r.1))
|
| 1214 |
+
| false, true, false =>
|
| 1215 |
+
if uvars.isEmpty then
|
| 1216 |
+
return toDoElems (β `(do let r β $term:term; return r))
|
| 1217 |
+
else
|
| 1218 |
+
return toDoElems (β `(do let r β $term:term; $u:term := r.2; return r.1))
|
| 1219 |
+
| false, false, true => toDoElems <$>
|
| 1220 |
+
`(do let r β $term:term;
|
| 1221 |
+
match r with
|
| 1222 |
+
| .break u => $u:term := u; break
|
| 1223 |
+
| .continue u => $u:term := u; continue)
|
| 1224 |
+
| true, true, false => toDoElems <$>
|
| 1225 |
+
`(do let r β $term:term;
|
| 1226 |
+
match r with
|
| 1227 |
+
| .pure a u => $u:term := u; pure a
|
| 1228 |
+
| .return b u => $u:term := u; return b)
|
| 1229 |
+
| true, false, true => toDoElems <$>
|
| 1230 |
+
`(do let r β $term:term;
|
| 1231 |
+
match r with
|
| 1232 |
+
| .pureReturn a u => $u:term := u; pure a
|
| 1233 |
+
| .break u => $u:term := u; break
|
| 1234 |
+
| .continue u => $u:term := u; continue)
|
| 1235 |
+
| false, true, true => toDoElems <$>
|
| 1236 |
+
`(do let r β $term:term;
|
| 1237 |
+
match r with
|
| 1238 |
+
| .pureReturn a u => $u:term := u; return a
|
| 1239 |
+
| .break u => $u:term := u; break
|
| 1240 |
+
| .continue u => $u:term := u; continue)
|
| 1241 |
+
| true, true, true => toDoElems <$>
|
| 1242 |
+
`(do let r β $term:term;
|
| 1243 |
+
match r with
|
| 1244 |
+
| .pure a u => $u:term := u; pure a
|
| 1245 |
+
| .return a u => $u:term := u; return a
|
| 1246 |
+
| .break u => $u:term := u; break
|
| 1247 |
+
| .continue u => $u:term := u; continue)
|
| 1248 |
+
| false, false, false => unreachable!
|
| 1249 |
+
|
| 1250 |
+
end ToTerm
|
| 1251 |
+
|
| 1252 |
+
def isMutableLet (doElem : Syntax) : Bool :=
|
| 1253 |
+
let kind := doElem.getKind
|
| 1254 |
+
(kind == ``doLetArrow || kind == ``doLet || kind == ``doLetElse)
|
| 1255 |
+
&&
|
| 1256 |
+
!doElem[1].isNone
|
| 1257 |
+
|
| 1258 |
+
namespace ToCodeBlock
|
| 1259 |
+
|
| 1260 |
+
structure Context where
|
| 1261 |
+
ref : Syntax
|
| 1262 |
+
/-- Syntax representing the monad associated with the do notation. -/
|
| 1263 |
+
m : Syntax
|
| 1264 |
+
/-- Syntax to reference the result of the monadic computation performed by the do notation. -/
|
| 1265 |
+
returnType : Syntax
|
| 1266 |
+
mutableVars : VarSet := {}
|
| 1267 |
+
insideFor : Bool := false
|
| 1268 |
+
|
| 1269 |
+
abbrev M := ReaderT Context TermElabM
|
| 1270 |
+
|
| 1271 |
+
def withNewMutableVars {Ξ±} (newVars : Array Var) (mutable : Bool) (x : M Ξ±) : M Ξ± :=
|
| 1272 |
+
withReader (fun ctx => if mutable then { ctx with mutableVars := insertVars ctx.mutableVars newVars } else ctx) x
|
| 1273 |
+
|
| 1274 |
+
def checkReassignable (xs : Array Var) : M Unit := do
|
| 1275 |
+
let throwInvalidReassignment (x : Name) : M Unit :=
|
| 1276 |
+
throwError "`{x.simpMacroScopes}` cannot be mutated, only variables declared using `let mut` can be mutated. If you did not intend to mutate but define `{x.simpMacroScopes}`, consider using `let {x.simpMacroScopes}` instead"
|
| 1277 |
+
let ctx β read
|
| 1278 |
+
for x in xs do
|
| 1279 |
+
unless ctx.mutableVars.contains x.getId do
|
| 1280 |
+
throwInvalidReassignment x.getId
|
| 1281 |
+
|
| 1282 |
+
def checkNotShadowingMutable (xs : Array Var) : M Unit := do
|
| 1283 |
+
let throwInvalidShadowing (x : Name) : M Unit :=
|
| 1284 |
+
throwError "mutable variable `{x.simpMacroScopes}` cannot be shadowed"
|
| 1285 |
+
let ctx β read
|
| 1286 |
+
for x in xs do
|
| 1287 |
+
if ctx.mutableVars.contains x.getId then
|
| 1288 |
+
withRef x <| throwInvalidShadowing x.getId
|
| 1289 |
+
|
| 1290 |
+
def withFor {Ξ±} (x : M Ξ±) : M Ξ± :=
|
| 1291 |
+
withReader (fun ctx => { ctx with insideFor := true }) x
|
| 1292 |
+
|
| 1293 |
+
structure ToForInTermResult where
|
| 1294 |
+
uvars : Array Var
|
| 1295 |
+
term : Syntax
|
| 1296 |
+
|
| 1297 |
+
def mkForInBody (_ : Syntax) (forInBody : CodeBlock) : M ToForInTermResult := do
|
| 1298 |
+
let ctx β read
|
| 1299 |
+
let uvars := forInBody.uvars
|
| 1300 |
+
let uvars := varSetToArray uvars
|
| 1301 |
+
let term β liftMacroM <| ToTerm.run forInBody.code ctx.m ctx.returnType uvars (if hasReturn forInBody.code then ToTerm.Kind.forInWithReturn else ToTerm.Kind.forIn)
|
| 1302 |
+
return β¨uvars, termβ©
|
| 1303 |
+
|
| 1304 |
+
def ensureInsideFor : M Unit :=
|
| 1305 |
+
unless (β read).insideFor do
|
| 1306 |
+
throwError "invalid `do` element, it must be inside `for`"
|
| 1307 |
+
|
| 1308 |
+
def ensureEOS (doElems : List Syntax) : M Unit :=
|
| 1309 |
+
unless doElems.isEmpty do
|
| 1310 |
+
throwError "must be last element in a `do` sequence"
|
| 1311 |
+
|
| 1312 |
+
variable (baseId : Name) in
|
| 1313 |
+
private partial def expandLiftMethodAux (inQuot : Bool) (inBinder : Bool) : Syntax β StateT (List Syntax) M Syntax
|
| 1314 |
+
| stx@(Syntax.node i k args) =>
|
| 1315 |
+
if k == choiceKind then do
|
| 1316 |
+
-- choice node: check that lifts are consistent
|
| 1317 |
+
let alts β stx.getArgs.mapM (expandLiftMethodAux inQuot inBinder Β· |>.run [])
|
| 1318 |
+
let (_, lifts) := alts[0]!
|
| 1319 |
+
unless alts.all (Β·.2 == lifts) do
|
| 1320 |
+
throwErrorAt stx "cannot lift `(<- ...)` over inconsistent syntax variants, consider lifting out the binding manually"
|
| 1321 |
+
modify (Β· ++ lifts)
|
| 1322 |
+
return .node i k (alts.map (Β·.1))
|
| 1323 |
+
else if liftMethodDelimiter k then
|
| 1324 |
+
return stx
|
| 1325 |
+
-- For `pure` if-then-else, we only lift `(<- ...)` occurring in the condition.
|
| 1326 |
+
else if h : args.size >= 2 β§ (k == ``termDepIfThenElse || k == ``termIfThenElse) then do
|
| 1327 |
+
let inAntiquot := stx.isAntiquot && !stx.isEscapedAntiquot
|
| 1328 |
+
let arg1 β expandLiftMethodAux (inQuot && !inAntiquot || stx.isQuot) inBinder args[1]
|
| 1329 |
+
let args := args.set! 1 arg1
|
| 1330 |
+
return Syntax.node i k args
|
| 1331 |
+
else if k == ``Parser.Term.liftMethod && !inQuot then withFreshMacroScope do
|
| 1332 |
+
if inBinder then
|
| 1333 |
+
throwErrorAt stx "cannot lift `(<- ...)` over a binder, this error usually happens when you are trying to lift a method nested in a `fun`, `let`, or `match`-alternative, and it can often be fixed by adding a missing `do`"
|
| 1334 |
+
let term := args[1]!
|
| 1335 |
+
let term β expandLiftMethodAux inQuot inBinder term
|
| 1336 |
+
-- keep name deterministic across choice branches
|
| 1337 |
+
let id β mkIdentFromRef (.num baseId (β get).length)
|
| 1338 |
+
let auxDoElem : Syntax β `(doElem| let $id:ident β $term:term)
|
| 1339 |
+
modify fun s => s ++ [auxDoElem]
|
| 1340 |
+
return id
|
| 1341 |
+
else do
|
| 1342 |
+
let inAntiquot := stx.isAntiquot && !stx.isEscapedAntiquot
|
| 1343 |
+
let inBinder := inBinder || (!inQuot && liftMethodForbiddenBinder stx)
|
| 1344 |
+
let args β args.mapM (expandLiftMethodAux (inQuot && !inAntiquot || stx.isQuot) inBinder)
|
| 1345 |
+
return Syntax.node i k args
|
| 1346 |
+
| stx => return stx
|
| 1347 |
+
|
| 1348 |
+
def expandLiftMethod (doElem : Syntax) : M (List Syntax Γ Syntax) := do
|
| 1349 |
+
if !hasLiftMethod doElem then
|
| 1350 |
+
return ([], doElem)
|
| 1351 |
+
else
|
| 1352 |
+
let baseId β withFreshMacroScope (MonadQuotation.addMacroScope `__do_lift)
|
| 1353 |
+
let (doElem, doElemsNew) β (expandLiftMethodAux baseId false false doElem).run []
|
| 1354 |
+
return (doElemsNew, doElem)
|
| 1355 |
+
|
| 1356 |
+
def checkLetArrowRHS (doElem : Syntax) : M Unit := do
|
| 1357 |
+
let kind := doElem.getKind
|
| 1358 |
+
if kind == ``Parser.Term.doLetArrow ||
|
| 1359 |
+
kind == ``Parser.Term.doLet ||
|
| 1360 |
+
kind == ``Parser.Term.doLetRec ||
|
| 1361 |
+
kind == ``Parser.Term.doHave ||
|
| 1362 |
+
kind == ``Parser.Term.doReassign ||
|
| 1363 |
+
kind == ``Parser.Term.doReassignArrow then
|
| 1364 |
+
throwErrorAt doElem "invalid kind of value `{kind}` in an assignment"
|
| 1365 |
+
|
| 1366 |
+
/-- Generate `CodeBlock` for `doReturn` which is of the form
|
| 1367 |
+
```
|
| 1368 |
+
"return " >> optional termParser
|
| 1369 |
+
```
|
| 1370 |
+
`doElems` is only used for sanity checking. -/
|
| 1371 |
+
def doReturnToCode (doReturn : Syntax) (doElems: List Syntax) : M CodeBlock := withRef doReturn do
|
| 1372 |
+
ensureEOS doElems
|
| 1373 |
+
let argOpt := doReturn[1]
|
| 1374 |
+
let arg β if argOpt.isNone then liftMacroM mkUnit else pure argOpt[0]
|
| 1375 |
+
return mkReturn (β getRef) arg
|
| 1376 |
+
|
| 1377 |
+
structure Catch where
|
| 1378 |
+
x : Syntax
|
| 1379 |
+
optType : Syntax
|
| 1380 |
+
codeBlock : CodeBlock
|
| 1381 |
+
|
| 1382 |
+
def getTryCatchUpdatedVars (tryCode : CodeBlock) (catches : Array Catch) (finallyCode? : Option CodeBlock) : VarSet :=
|
| 1383 |
+
let ws := tryCode.uvars
|
| 1384 |
+
let ws := catches.foldl (init := ws) fun ws alt => union alt.codeBlock.uvars ws
|
| 1385 |
+
let ws := match finallyCode? with
|
| 1386 |
+
| none => ws
|
| 1387 |
+
| some c => union c.uvars ws
|
| 1388 |
+
ws
|
| 1389 |
+
|
| 1390 |
+
def tryCatchPred (tryCode : CodeBlock) (catches : Array Catch) (finallyCode? : Option CodeBlock) (p : Code β Bool) : Bool :=
|
| 1391 |
+
p tryCode.code ||
|
| 1392 |
+
catches.any (fun Β«catchΒ» => p Β«catchΒ».codeBlock.code) ||
|
| 1393 |
+
match finallyCode? with
|
| 1394 |
+
| none => false
|
| 1395 |
+
| some finallyCode => p finallyCode.code
|
| 1396 |
+
|
| 1397 |
+
mutual
|
| 1398 |
+
/-- "Concatenate" `c` with `doSeqToCode doElems` -/
|
| 1399 |
+
partial def concatWith (c : CodeBlock) (doElems : List Syntax) : M CodeBlock :=
|
| 1400 |
+
match doElems with
|
| 1401 |
+
| [] => pure c
|
| 1402 |
+
| nextDoElem :: _ => do
|
| 1403 |
+
let k β doSeqToCode doElems
|
| 1404 |
+
let ref := nextDoElem
|
| 1405 |
+
concat c ref none k
|
| 1406 |
+
|
| 1407 |
+
/-- Generate `CodeBlock` for `doLetArrow; doElems`
|
| 1408 |
+
`doLetArrow` is of the form
|
| 1409 |
+
```
|
| 1410 |
+
"let " >> optional "mut " >> (doIdDecl <|> doPatDecl)
|
| 1411 |
+
```
|
| 1412 |
+
where
|
| 1413 |
+
```
|
| 1414 |
+
def doIdDecl := leading_parser ident >> optType >> leftArrow >> doElemParser
|
| 1415 |
+
def doPatDecl := leading_parser termParser >> leftArrow >> doElemParser >> optional (" | " >> doSeq)
|
| 1416 |
+
```
|
| 1417 |
+
-/
|
| 1418 |
+
partial def doLetArrowToCode (doLetArrow : Syntax) (doElems : List Syntax) : M CodeBlock := do
|
| 1419 |
+
let decl := doLetArrow[2]
|
| 1420 |
+
if decl.getKind == ``Parser.Term.doIdDecl then
|
| 1421 |
+
let y := decl[0]
|
| 1422 |
+
checkNotShadowingMutable #[y]
|
| 1423 |
+
let doElem := decl[3]
|
| 1424 |
+
let k β withNewMutableVars #[y] (isMutableLet doLetArrow) (doSeqToCode doElems)
|
| 1425 |
+
match isDoExpr? doElem with
|
| 1426 |
+
| some _ => return mkVarDeclCore #[y] doLetArrow k
|
| 1427 |
+
| none =>
|
| 1428 |
+
checkLetArrowRHS doElem
|
| 1429 |
+
let c β doSeqToCode [doElem]
|
| 1430 |
+
match doElems with
|
| 1431 |
+
| [] => pure c
|
| 1432 |
+
| kRef::_ => concat c kRef y k
|
| 1433 |
+
else if decl.getKind == ``Parser.Term.doPatDecl then
|
| 1434 |
+
let pattern := decl[0]
|
| 1435 |
+
let doElem := decl[2]
|
| 1436 |
+
let optElse := decl[3]
|
| 1437 |
+
if optElse.isNone then withFreshMacroScope do
|
| 1438 |
+
let auxDo β if isMutableLet doLetArrow then
|
| 1439 |
+
`(do let%$doLetArrow __discr β $doElem; let%$doLetArrow mut $pattern:term := __discr)
|
| 1440 |
+
else
|
| 1441 |
+
`(do let%$doLetArrow __discr β $doElem; let%$doLetArrow $pattern:term := __discr)
|
| 1442 |
+
doSeqToCode <| getDoSeqElems (getDoSeq auxDo) ++ doElems
|
| 1443 |
+
else
|
| 1444 |
+
let contSeq β if isMutableLet doLetArrow then
|
| 1445 |
+
let vars β (β getPatternVarsEx pattern).mapM fun var => `(doElem| let mut $var := $var)
|
| 1446 |
+
pure (vars ++ doElems.toArray)
|
| 1447 |
+
else
|
| 1448 |
+
pure doElems.toArray
|
| 1449 |
+
let contSeq := mkDoSeq contSeq
|
| 1450 |
+
let elseSeq := optElse[1]
|
| 1451 |
+
let auxDo β `(do let%$doLetArrow __discr β $doElem; match%$doLetArrow __discr with | $pattern:term => $contSeq | _ => $elseSeq)
|
| 1452 |
+
doSeqToCode <| getDoSeqElems (getDoSeq auxDo)
|
| 1453 |
+
else
|
| 1454 |
+
throwError "unexpected kind of `do` declaration"
|
| 1455 |
+
|
| 1456 |
+
partial def doLetElseToCode (doLetElse : Syntax) (doElems : List Syntax) : M CodeBlock := do
|
| 1457 |
+
-- "let " >> optional "mut " >> termParser >> " := " >> termParser >> checkColGt >> " | " >> doSeq
|
| 1458 |
+
let pattern := doLetElse[2]
|
| 1459 |
+
let val := doLetElse[4]
|
| 1460 |
+
let elseSeq := doLetElse[6]
|
| 1461 |
+
let contSeq β if isMutableLet doLetElse then
|
| 1462 |
+
let vars β (β getPatternVarsEx pattern).mapM fun var => `(doElem| let mut $var := $var)
|
| 1463 |
+
pure (vars ++ doElems.toArray)
|
| 1464 |
+
else
|
| 1465 |
+
pure doElems.toArray
|
| 1466 |
+
let contSeq := mkDoSeq contSeq
|
| 1467 |
+
let auxDo β `(do match $val:term with | $pattern:term => $contSeq | _ => $elseSeq)
|
| 1468 |
+
doSeqToCode <| getDoSeqElems (getDoSeq auxDo)
|
| 1469 |
+
|
| 1470 |
+
/-- Generate `CodeBlock` for `doReassignArrow; doElems`
|
| 1471 |
+
`doReassignArrow` is of the form
|
| 1472 |
+
```
|
| 1473 |
+
(doIdDecl <|> doPatDecl)
|
| 1474 |
+
```
|
| 1475 |
+
-/
|
| 1476 |
+
partial def doReassignArrowToCode (doReassignArrow : Syntax) (doElems : List Syntax) : M CodeBlock := do
|
| 1477 |
+
let decl := doReassignArrow[0]
|
| 1478 |
+
if decl.getKind == ``Parser.Term.doIdDecl then
|
| 1479 |
+
let doElem := decl[3]
|
| 1480 |
+
let y := decl[0]
|
| 1481 |
+
let auxDo β `(do let r β $doElem; $y:ident := r)
|
| 1482 |
+
doSeqToCode <| getDoSeqElems (getDoSeq auxDo) ++ doElems
|
| 1483 |
+
else if decl.getKind == ``Parser.Term.doPatDecl then
|
| 1484 |
+
let pattern := decl[0]
|
| 1485 |
+
let doElem := decl[2]
|
| 1486 |
+
let optElse := decl[3]
|
| 1487 |
+
if optElse.isNone then withFreshMacroScope do
|
| 1488 |
+
let auxDo β `(do let __discr β $doElem; $pattern:term := __discr)
|
| 1489 |
+
doSeqToCode <| getDoSeqElems (getDoSeq auxDo) ++ doElems
|
| 1490 |
+
else
|
| 1491 |
+
throwError "reassignment with `|` (i.e., \"else clause\") is not currently supported"
|
| 1492 |
+
else
|
| 1493 |
+
throwError "unexpected kind of `do` reassignment"
|
| 1494 |
+
|
| 1495 |
+
/-- Generate `CodeBlock` for `doIf; doElems`
|
| 1496 |
+
`doIf` is of the form
|
| 1497 |
+
```
|
| 1498 |
+
"if " >> optIdent >> termParser >> " then " >> doSeq
|
| 1499 |
+
>> many (group (try (group (" else " >> " if ")) >> optIdent >> termParser >> " then " >> doSeq))
|
| 1500 |
+
>> optional (" else " >> doSeq)
|
| 1501 |
+
``` -/
|
| 1502 |
+
partial def doIfToCode (doIf : Syntax) (doElems : List Syntax) : M CodeBlock := do
|
| 1503 |
+
let view := mkDoIfView doIf
|
| 1504 |
+
let thenBranch β doSeqToCode (getDoSeqElems view.thenBranch)
|
| 1505 |
+
let elseBranch β doSeqToCode (getDoSeqElems view.elseBranch)
|
| 1506 |
+
let ite β mkIte view.ref view.optIdent view.cond thenBranch elseBranch
|
| 1507 |
+
concatWith ite doElems
|
| 1508 |
+
|
| 1509 |
+
/-- Generate `CodeBlock` for `doUnless; doElems`
|
| 1510 |
+
`doUnless` is of the form
|
| 1511 |
+
```
|
| 1512 |
+
"unless " >> termParser >> "do " >> doSeq
|
| 1513 |
+
``` -/
|
| 1514 |
+
partial def doUnlessToCode (doUnless : Syntax) (doElems : List Syntax) : M CodeBlock := withRef doUnless do
|
| 1515 |
+
let cond := doUnless[1]
|
| 1516 |
+
let doSeq := doUnless[3]
|
| 1517 |
+
let body β doSeqToCode (getDoSeqElems doSeq)
|
| 1518 |
+
let unlessCode β liftMacroM <| mkUnless cond body
|
| 1519 |
+
concatWith unlessCode doElems
|
| 1520 |
+
|
| 1521 |
+
/-- Generate `CodeBlock` for `doFor; doElems`
|
| 1522 |
+
`doFor` is of the form
|
| 1523 |
+
```
|
| 1524 |
+
def doForDecl := leading_parser termParser >> " in " >> withForbidden "do" termParser
|
| 1525 |
+
def doFor := leading_parser "for " >> sepBy1 doForDecl ", " >> "do " >> doSeq
|
| 1526 |
+
```
|
| 1527 |
+
-/
|
| 1528 |
+
partial def doForToCode (doFor : Syntax) (doElems : List Syntax) : M CodeBlock := do
|
| 1529 |
+
let doForDecls := doFor[1].getSepArgs
|
| 1530 |
+
if h : doForDecls.size > 1 then
|
| 1531 |
+
/-
|
| 1532 |
+
Expand
|
| 1533 |
+
```
|
| 1534 |
+
for x in xs, y in ys do
|
| 1535 |
+
body
|
| 1536 |
+
```
|
| 1537 |
+
into
|
| 1538 |
+
```
|
| 1539 |
+
let s := toStream ys
|
| 1540 |
+
for x in xs do
|
| 1541 |
+
match Stream.next? s with
|
| 1542 |
+
| none => break
|
| 1543 |
+
| some (y, s') =>
|
| 1544 |
+
s := s'
|
| 1545 |
+
body
|
| 1546 |
+
```
|
| 1547 |
+
-/
|
| 1548 |
+
-- Extract second element
|
| 1549 |
+
let doForDecl := doForDecls[1]!
|
| 1550 |
+
unless doForDecl[0].isNone do
|
| 1551 |
+
throwErrorAt doForDecl[0] "the proof annotation here has not been implemented yet"
|
| 1552 |
+
let y := doForDecl[1]
|
| 1553 |
+
let ys := doForDecl[3]
|
| 1554 |
+
let doForDecls := doForDecls.eraseIdx 1
|
| 1555 |
+
let body := doFor[3]
|
| 1556 |
+
withFreshMacroScope do
|
| 1557 |
+
/- Recall that `@` (explicit) disables `coeAtOutParam`.
|
| 1558 |
+
We used `@` at `Stream` functions to make sure `resultIsOutParamSupport` is not used. -/
|
| 1559 |
+
let toStreamApp β withRef ys `(@toStream _ _ _ $ys)
|
| 1560 |
+
let auxDo β
|
| 1561 |
+
`(do let mut s := $toStreamApp:term
|
| 1562 |
+
for $doForDecls:doForDecl,* do
|
| 1563 |
+
match @Stream.next? _ _ _ s with
|
| 1564 |
+
| none => break
|
| 1565 |
+
| some ($y, s') =>
|
| 1566 |
+
s := s'
|
| 1567 |
+
do $body)
|
| 1568 |
+
doSeqToCode (getDoSeqElems (getDoSeq auxDo) ++ doElems)
|
| 1569 |
+
else withRef doFor do
|
| 1570 |
+
let h? := if doForDecls[0]![0].isNone then none else some doForDecls[0]![0][0]
|
| 1571 |
+
let x := doForDecls[0]![1]
|
| 1572 |
+
withRef x <| checkNotShadowingMutable (β getPatternVarsEx x)
|
| 1573 |
+
let xs := doForDecls[0]![3]
|
| 1574 |
+
let forElems := getDoSeqElems doFor[3]
|
| 1575 |
+
let forInBodyCodeBlock β withFor (doSeqToCode forElems)
|
| 1576 |
+
let β¨uvars, forInBodyβ© β mkForInBody x forInBodyCodeBlock
|
| 1577 |
+
let ctx β read
|
| 1578 |
+
-- semantic no-op that replaces the `uvars`' position information (which all point inside the loop)
|
| 1579 |
+
-- with that of the respective mutable declarations outside the loop, which allows the language
|
| 1580 |
+
-- server to identify them as conceptually identical variables
|
| 1581 |
+
let uvars := uvars.map fun v => ctx.mutableVars.findD v.getId v
|
| 1582 |
+
let uvarsTuple β liftMacroM do mkTuple uvars
|
| 1583 |
+
if hasReturn forInBodyCodeBlock.code then
|
| 1584 |
+
let forInBody β liftMacroM <| destructTuple uvars (β `(r)) forInBody
|
| 1585 |
+
let optType β `(Option $((β read).returnType))
|
| 1586 |
+
let forInTerm β if let some h := h? then
|
| 1587 |
+
annotate doFor
|
| 1588 |
+
(β `(for_in'% $(xs) (MProd.mk (none : $optType) $uvarsTuple) fun $x $h (r : MProd $optType _) => let r := r.2; $forInBody))
|
| 1589 |
+
else
|
| 1590 |
+
annotate doFor
|
| 1591 |
+
(β `(for_in% $(xs) (MProd.mk (none : $optType) $uvarsTuple) fun $x (r : MProd $optType _) => let r := r.2; $forInBody))
|
| 1592 |
+
let auxDo β `(do let r β $forInTerm:term;
|
| 1593 |
+
$uvarsTuple:term := r.2;
|
| 1594 |
+
match r.1 with
|
| 1595 |
+
| none => Pure.pure (ensure_expected_type% "type mismatch, `for`" PUnit.unit)
|
| 1596 |
+
| some a => return ensure_expected_type% "type mismatch, `for`" a)
|
| 1597 |
+
doSeqToCode (getDoSeqElems (getDoSeq auxDo) ++ doElems)
|
| 1598 |
+
else
|
| 1599 |
+
let forInBody β liftMacroM <| destructTuple uvars (β `(r)) forInBody
|
| 1600 |
+
let forInTerm β if let some h := h? then
|
| 1601 |
+
annotate doFor (β `(for_in'% $(xs) $uvarsTuple fun $x $h r => $forInBody))
|
| 1602 |
+
else
|
| 1603 |
+
annotate doFor (β `(for_in% $(xs) $uvarsTuple fun $x r => $forInBody))
|
| 1604 |
+
if doElems.isEmpty then
|
| 1605 |
+
let auxDo β `(do let r β $forInTerm:term;
|
| 1606 |
+
$uvarsTuple:term := r;
|
| 1607 |
+
Pure.pure (ensure_expected_type% "type mismatch, `for`" PUnit.unit))
|
| 1608 |
+
doSeqToCode <| getDoSeqElems (getDoSeq auxDo)
|
| 1609 |
+
else
|
| 1610 |
+
let auxDo β `(do let r β $forInTerm:term; $uvarsTuple:term := r)
|
| 1611 |
+
doSeqToCode <| getDoSeqElems (getDoSeq auxDo) ++ doElems
|
| 1612 |
+
|
| 1613 |
+
/-- Generate `CodeBlock` for `doMatch; doElems` -/
|
| 1614 |
+
partial def doMatchToCode (doMatch : Syntax) (doElems: List Syntax) : M CodeBlock := do
|
| 1615 |
+
let ref := doMatch
|
| 1616 |
+
let genParam := doMatch[1]
|
| 1617 |
+
let optMotive := doMatch[2]
|
| 1618 |
+
let discrs := doMatch[3]
|
| 1619 |
+
let matchAlts := doMatch[5][0].getArgs -- Array of `doMatchAlt`
|
| 1620 |
+
let matchAlts β matchAlts.foldlM (init := #[]) fun result matchAlt => return result ++ (β liftMacroM <| expandMatchAlt matchAlt)
|
| 1621 |
+
let alts β matchAlts.mapM fun matchAlt => do
|
| 1622 |
+
let patterns := matchAlt[1][0]
|
| 1623 |
+
let vars β getPatternsVarsEx patterns.getSepArgs
|
| 1624 |
+
withRef patterns <| checkNotShadowingMutable vars
|
| 1625 |
+
let rhs := matchAlt[3]
|
| 1626 |
+
let rhs β doSeqToCode (getDoSeqElems rhs)
|
| 1627 |
+
pure { ref := matchAlt, vars := vars, patterns := patterns, rhs := rhs : Alt CodeBlock }
|
| 1628 |
+
let matchCode β mkMatch ref genParam discrs optMotive alts
|
| 1629 |
+
concatWith matchCode doElems
|
| 1630 |
+
|
| 1631 |
+
/-- Generate `CodeBlock` for `doMatchExpr; doElems` -/
|
| 1632 |
+
partial def doMatchExprToCode (doMatchExpr : Syntax) (doElems: List Syntax) : M CodeBlock := do
|
| 1633 |
+
let ref := doMatchExpr
|
| 1634 |
+
let Β«metaΒ» := doMatchExpr[1].isNone
|
| 1635 |
+
let discr := doMatchExpr[2]
|
| 1636 |
+
let alts := doMatchExpr[4][0].getArgs -- Array of `doMatchExprAlt`
|
| 1637 |
+
let alts β alts.mapM fun alt => do
|
| 1638 |
+
let pat := alt[1]
|
| 1639 |
+
let var? := if pat[0].isNone then none else some pat[0][0]
|
| 1640 |
+
let funName := pat[1]
|
| 1641 |
+
let pvars := pat[2].getArgs
|
| 1642 |
+
let rhs := alt[3]
|
| 1643 |
+
let rhs β doSeqToCode (getDoSeqElems rhs)
|
| 1644 |
+
pure { ref, var?, funName, pvars, rhs }
|
| 1645 |
+
let elseBranch β doSeqToCode (getDoSeqElems doMatchExpr[4][1][3])
|
| 1646 |
+
let matchCode β mkMatchExpr ref Β«metaΒ» discr alts elseBranch
|
| 1647 |
+
concatWith matchCode doElems
|
| 1648 |
+
|
| 1649 |
+
/--
|
| 1650 |
+
Generate `CodeBlock` for `doTry; doElems`
|
| 1651 |
+
```
|
| 1652 |
+
def doTry := leading_parser "try " >> doSeq >> many (doCatch <|> doCatchMatch) >> optional doFinally
|
| 1653 |
+
def doCatch := leading_parser "catch " >> binderIdent >> optional (":" >> termParser) >> darrow >> doSeq
|
| 1654 |
+
def doCatchMatch := leading_parser "catch " >> doMatchAlts
|
| 1655 |
+
def doFinally := leading_parser "finally " >> doSeq
|
| 1656 |
+
```
|
| 1657 |
+
-/
|
| 1658 |
+
partial def doTryToCode (doTry : Syntax) (doElems: List Syntax) : M CodeBlock := do
|
| 1659 |
+
let tryCode β doSeqToCode (getDoSeqElems doTry[1])
|
| 1660 |
+
let optFinally := doTry[3]
|
| 1661 |
+
let catches β doTry[2].getArgs.mapM fun catchStx : Syntax => do
|
| 1662 |
+
if catchStx.getKind == ``Parser.Term.doCatch then
|
| 1663 |
+
let x := catchStx[1]
|
| 1664 |
+
if x.isIdent then
|
| 1665 |
+
withRef x <| checkNotShadowingMutable #[x]
|
| 1666 |
+
let optType := catchStx[2]
|
| 1667 |
+
let c β doSeqToCode (getDoSeqElems catchStx[4])
|
| 1668 |
+
return { x := x, optType := optType, codeBlock := c : Catch }
|
| 1669 |
+
else if catchStx.getKind == ``Parser.Term.doCatchMatch then
|
| 1670 |
+
let matchAlts := catchStx[1]
|
| 1671 |
+
let x β `(ex)
|
| 1672 |
+
let auxDo β `(do match ex with $matchAlts)
|
| 1673 |
+
let c β doSeqToCode (getDoSeqElems (getDoSeq auxDo))
|
| 1674 |
+
return { x := x, codeBlock := c, optType := mkNullNode : Catch }
|
| 1675 |
+
else
|
| 1676 |
+
throwError "unexpected kind of `catch`"
|
| 1677 |
+
let finallyCode? β if optFinally.isNone then pure none else some <$> doSeqToCode (getDoSeqElems optFinally[0][1])
|
| 1678 |
+
if catches.isEmpty && finallyCode?.isNone then
|
| 1679 |
+
throwError "invalid `try`, it must have a `catch` or `finally`"
|
| 1680 |
+
let ctx β read
|
| 1681 |
+
let ws := getTryCatchUpdatedVars tryCode catches finallyCode?
|
| 1682 |
+
let uvars := varSetToArray ws
|
| 1683 |
+
let a := tryCatchPred tryCode catches finallyCode? hasTerminalAction
|
| 1684 |
+
let r := tryCatchPred tryCode catches finallyCode? hasReturn
|
| 1685 |
+
let bc := tryCatchPred tryCode catches finallyCode? hasBreakContinue
|
| 1686 |
+
let toTerm (codeBlock : CodeBlock) : M Syntax := do
|
| 1687 |
+
let codeBlock β liftM $ extendUpdatedVars codeBlock ws
|
| 1688 |
+
liftMacroM <| ToTerm.mkNestedTerm codeBlock.code ctx.m ctx.returnType uvars a r bc
|
| 1689 |
+
let term β toTerm tryCode
|
| 1690 |
+
let term β catches.foldlM (init := term) fun term Β«catchΒ» => do
|
| 1691 |
+
let catchTerm β toTerm Β«catchΒ».codeBlock
|
| 1692 |
+
if catch.optType.isNone then
|
| 1693 |
+
annotate doTry (β ``(MonadExcept.tryCatch $term (fun $(Β«catchΒ».x):ident => $catchTerm)))
|
| 1694 |
+
else
|
| 1695 |
+
let type := Β«catchΒ».optType[1]
|
| 1696 |
+
annotate doTry (β ``(tryCatchThe $type $term (fun $(Β«catchΒ».x):ident => $catchTerm)))
|
| 1697 |
+
let term β match finallyCode? with
|
| 1698 |
+
| none => pure term
|
| 1699 |
+
| some finallyCode => withRef optFinally do
|
| 1700 |
+
unless finallyCode.uvars.isEmpty do
|
| 1701 |
+
throwError "`finally` currently does not support reassignments"
|
| 1702 |
+
if hasBreakContinueReturn finallyCode.code then
|
| 1703 |
+
throwError "`finally` currently does `return`, `break`, nor `continue`"
|
| 1704 |
+
let finallyTerm β liftMacroM <| ToTerm.run finallyCode.code ctx.m ctx.returnType {} ToTerm.Kind.regular
|
| 1705 |
+
annotate doTry (β ``(tryFinally $term $finallyTerm))
|
| 1706 |
+
let doElemsNew β liftMacroM <| ToTerm.matchNestedTermResult term uvars a r bc
|
| 1707 |
+
doSeqToCode (doElemsNew ++ doElems)
|
| 1708 |
+
|
| 1709 |
+
partial def doSeqToCode : List Syntax β M CodeBlock
|
| 1710 |
+
| [] => do liftMacroM mkPureUnitAction
|
| 1711 |
+
| doElem::doElems => withIncRecDepth <| withRef doElem do
|
| 1712 |
+
checkSystem "`do`-expander"
|
| 1713 |
+
match (β liftMacroM <| expandMacro? doElem) with
|
| 1714 |
+
| some doElem => doSeqToCode (doElem::doElems)
|
| 1715 |
+
| none =>
|
| 1716 |
+
match (β liftMacroM <| expandDoIf? doElem) with
|
| 1717 |
+
| some doElem => doSeqToCode (doElem::doElems)
|
| 1718 |
+
| none =>
|
| 1719 |
+
match (β liftMacroM <| expandDoLetExpr? doElem doElems) with
|
| 1720 |
+
| some doElem => doSeqToCode [doElem]
|
| 1721 |
+
| none =>
|
| 1722 |
+
let (liftedDoElems, doElem) β expandLiftMethod doElem
|
| 1723 |
+
if !liftedDoElems.isEmpty then
|
| 1724 |
+
doSeqToCode (liftedDoElems ++ [doElem] ++ doElems)
|
| 1725 |
+
else
|
| 1726 |
+
let ref := doElem
|
| 1727 |
+
let k := doElem.getKind
|
| 1728 |
+
if k == ``Parser.Term.doLet then
|
| 1729 |
+
let vars β getDoLetVars doElem
|
| 1730 |
+
checkNotShadowingMutable vars
|
| 1731 |
+
mkVarDeclCore vars doElem <$> withNewMutableVars vars (isMutableLet doElem) (doSeqToCode doElems)
|
| 1732 |
+
else if k == ``Parser.Term.doHave then
|
| 1733 |
+
let vars β getDoHaveVars doElem
|
| 1734 |
+
checkNotShadowingMutable vars
|
| 1735 |
+
mkVarDeclCore vars doElem <$> (doSeqToCode doElems)
|
| 1736 |
+
else if k == ``Parser.Term.doLetRec then
|
| 1737 |
+
let vars β getDoLetRecVars doElem
|
| 1738 |
+
checkNotShadowingMutable vars
|
| 1739 |
+
mkVarDeclCore vars doElem <$> (doSeqToCode doElems)
|
| 1740 |
+
else if k == ``Parser.Term.doReassign then
|
| 1741 |
+
let vars β getDoReassignVars doElem
|
| 1742 |
+
checkReassignable vars
|
| 1743 |
+
let k β doSeqToCode doElems
|
| 1744 |
+
mkReassignCore vars doElem k
|
| 1745 |
+
else if k == ``Parser.Term.doLetArrow then
|
| 1746 |
+
doLetArrowToCode doElem doElems
|
| 1747 |
+
else if k == ``Parser.Term.doLetElse then
|
| 1748 |
+
doLetElseToCode doElem doElems
|
| 1749 |
+
else if k == ``Parser.Term.doReassignArrow then
|
| 1750 |
+
doReassignArrowToCode doElem doElems
|
| 1751 |
+
else if k == ``Parser.Term.doIf then
|
| 1752 |
+
doIfToCode doElem doElems
|
| 1753 |
+
else if k == ``Parser.Term.doUnless then
|
| 1754 |
+
doUnlessToCode doElem doElems
|
| 1755 |
+
else if k == ``Parser.Term.doFor then withFreshMacroScope do
|
| 1756 |
+
doForToCode doElem doElems
|
| 1757 |
+
else if k == ``Parser.Term.doMatch then
|
| 1758 |
+
doMatchToCode doElem doElems
|
| 1759 |
+
else if k == ``Parser.Term.doMatchExpr then
|
| 1760 |
+
doMatchExprToCode doElem doElems
|
| 1761 |
+
else if k == ``Parser.Term.doTry then
|
| 1762 |
+
doTryToCode doElem doElems
|
| 1763 |
+
else if k == ``Parser.Term.doBreak then
|
| 1764 |
+
ensureInsideFor
|
| 1765 |
+
ensureEOS doElems
|
| 1766 |
+
return mkBreak ref
|
| 1767 |
+
else if k == ``Parser.Term.doContinue then
|
| 1768 |
+
ensureInsideFor
|
| 1769 |
+
ensureEOS doElems
|
| 1770 |
+
return mkContinue ref
|
| 1771 |
+
else if k == ``Parser.Term.doReturn then
|
| 1772 |
+
doReturnToCode doElem doElems
|
| 1773 |
+
else if k == ``Parser.Term.doDbgTrace then
|
| 1774 |
+
return mkSeq doElem (β doSeqToCode doElems)
|
| 1775 |
+
else if k == ``Parser.Term.doAssert then
|
| 1776 |
+
return mkSeq doElem (β doSeqToCode doElems)
|
| 1777 |
+
else if k == ``Parser.Term.doDebugAssert then
|
| 1778 |
+
return mkSeq doElem (β doSeqToCode doElems)
|
| 1779 |
+
else if k == ``Parser.Term.doNested then
|
| 1780 |
+
let nestedDoSeq := doElem[1]
|
| 1781 |
+
doSeqToCode (getDoSeqElems nestedDoSeq ++ doElems)
|
| 1782 |
+
else if k == ``Parser.Term.doExpr then
|
| 1783 |
+
let term := doElem[0]
|
| 1784 |
+
if doElems.isEmpty then
|
| 1785 |
+
return mkTerminalAction term
|
| 1786 |
+
else
|
| 1787 |
+
return mkSeq term (β doSeqToCode doElems)
|
| 1788 |
+
else
|
| 1789 |
+
throwError "unexpected do-element of kind {doElem.getKind}:\n{doElem}"
|
| 1790 |
+
end
|
| 1791 |
+
|
| 1792 |
+
def run (doStx : Syntax) (m : Syntax) (returnType : Syntax) : TermElabM CodeBlock :=
|
| 1793 |
+
(doSeqToCode <| getDoSeqElems <| getDoSeq doStx).run { ref := doStx, m, returnType }
|
| 1794 |
+
|
| 1795 |
+
end ToCodeBlock
|
| 1796 |
+
|
| 1797 |
+
@[builtin_term_elab Β«doΒ»] def elabDo : TermElab := fun stx expectedType? => do
|
| 1798 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 1799 |
+
let bindInfo β extractBind expectedType?
|
| 1800 |
+
let m β Term.exprToSyntax bindInfo.m
|
| 1801 |
+
let returnType β Term.exprToSyntax bindInfo.returnType
|
| 1802 |
+
let codeBlock β ToCodeBlock.run stx m returnType
|
| 1803 |
+
let stxNew β liftMacroM <| ToTerm.run codeBlock.code m returnType
|
| 1804 |
+
trace[Elab.do] stxNew
|
| 1805 |
+
withMacroExpansion stx stxNew <| elabTermEnsuringType stxNew bindInfo.expectedType
|
| 1806 |
+
|
| 1807 |
+
end Do
|
| 1808 |
+
|
| 1809 |
+
builtin_initialize registerTraceClass `Elab.do
|
| 1810 |
+
|
| 1811 |
+
private def toDoElem (newKind : SyntaxNodeKind) : Macro := fun stx => do
|
| 1812 |
+
let stx := stx.setKind newKind
|
| 1813 |
+
withRef stx `(do $stx:doElem)
|
| 1814 |
+
|
| 1815 |
+
@[builtin_macro Lean.Parser.Term.termFor]
|
| 1816 |
+
def expandTermFor : Macro := toDoElem ``Parser.Term.doFor
|
| 1817 |
+
|
| 1818 |
+
@[builtin_macro Lean.Parser.Term.termTry]
|
| 1819 |
+
def expandTermTry : Macro := toDoElem ``Parser.Term.doTry
|
| 1820 |
+
|
| 1821 |
+
@[builtin_macro Lean.Parser.Term.termUnless]
|
| 1822 |
+
def expandTermUnless : Macro := toDoElem ``Parser.Term.doUnless
|
| 1823 |
+
|
| 1824 |
+
@[builtin_macro Lean.Parser.Term.termReturn]
|
| 1825 |
+
def expandTermReturn : Macro := toDoElem ``Parser.Term.doReturn
|
| 1826 |
+
|
| 1827 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/ElabRules.lean
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.MacroArgUtil
|
| 8 |
+
import Lean.Elab.AuxDef
|
| 9 |
+
|
| 10 |
+
namespace Lean.Elab.Command
|
| 11 |
+
open Lean.Syntax
|
| 12 |
+
open Lean.Parser.Term hiding macroArg
|
| 13 |
+
open Lean.Parser.Command
|
| 14 |
+
|
| 15 |
+
def elabElabRulesAux (doc? : Option (TSyntax ``docComment))
|
| 16 |
+
(attrs? : Option (TSepArray ``attrInstance ",")) (attrKind : TSyntax ``attrKind)
|
| 17 |
+
(k : SyntaxNodeKind) (cat? expty? : Option (Ident)) (alts : Array (TSyntax ``matchAlt)) :
|
| 18 |
+
CommandElabM Syntax := do
|
| 19 |
+
let alts β alts.mapM fun (alt : TSyntax ``matchAlt) => match alt with
|
| 20 |
+
| `(matchAltExpr| | $pats,* => $rhs) => do
|
| 21 |
+
let pat := pats.elemsAndSeps[0]!
|
| 22 |
+
if !pat.isQuot then
|
| 23 |
+
throwUnsupportedSyntax
|
| 24 |
+
let quoted := getQuotContent pat
|
| 25 |
+
let k' := quoted.getKind
|
| 26 |
+
if checkRuleKind k' k then
|
| 27 |
+
pure alt
|
| 28 |
+
else if k' == choiceKind then
|
| 29 |
+
match quoted.getArgs.find? fun quotAlt => checkRuleKind quotAlt.getKind k with
|
| 30 |
+
| none => throwErrorAt alt "invalid elab_rules alternative, expected syntax node kind '{k}'"
|
| 31 |
+
| some quoted =>
|
| 32 |
+
let pat := pat.setArg 1 quoted
|
| 33 |
+
let pats := β¨pats.elemsAndSeps.set! 0 patβ©
|
| 34 |
+
`(matchAltExpr| | $pats,* => $rhs)
|
| 35 |
+
else
|
| 36 |
+
throwErrorAt alt "invalid elab_rules alternative, unexpected syntax node kind '{k'}'"
|
| 37 |
+
| _ => throwUnsupportedSyntax
|
| 38 |
+
let catName β match cat?, expty? with
|
| 39 |
+
| some cat, _ => pure cat.getId
|
| 40 |
+
| _, some _ => pure `term
|
| 41 |
+
-- TODO: infer category from quotation kind, possibly even kind of quoted syntax?
|
| 42 |
+
| _, _ => throwError "invalid elab_rules command, specify category using `elab_rules : <cat> ...`"
|
| 43 |
+
let mkAttrs (kind : Name) : CommandElabM (TSyntaxArray ``attrInstance) := do
|
| 44 |
+
let attr β `(attrInstance| $attrKind:attrKind $(mkIdent kind):ident $(β mkIdentFromRef k):ident)
|
| 45 |
+
pure <| match attrs? with
|
| 46 |
+
| some attrs => attrs.getElems.push attr
|
| 47 |
+
| none => #[attr]
|
| 48 |
+
if let some expId := expty? then
|
| 49 |
+
if catName == `term then
|
| 50 |
+
`($[$doc?:docComment]? @[$(β mkAttrs `term_elab),*]
|
| 51 |
+
aux_def elabRules $(mkIdent k) : Lean.Elab.Term.TermElab :=
|
| 52 |
+
fun stx expectedType? => Lean.Elab.Term.withExpectedType expectedType? fun $expId => match stx with
|
| 53 |
+
$alts:matchAlt* | _ => no_error_if_unused% throwUnsupportedSyntax)
|
| 54 |
+
else
|
| 55 |
+
throwErrorAt expId "syntax category '{catName}' does not support expected type specification"
|
| 56 |
+
else if catName == `term then
|
| 57 |
+
`($[$doc?:docComment]? @[$(β mkAttrs `term_elab),*]
|
| 58 |
+
aux_def elabRules $(mkIdent k) : Lean.Elab.Term.TermElab :=
|
| 59 |
+
fun stx _ => match stx with
|
| 60 |
+
$alts:matchAlt* | _ => no_error_if_unused% throwUnsupportedSyntax)
|
| 61 |
+
else if catName == `command then
|
| 62 |
+
`($[$doc?:docComment]? @[$(β mkAttrs `command_elab),*]
|
| 63 |
+
aux_def elabRules $(mkIdent k) : Lean.Elab.Command.CommandElab :=
|
| 64 |
+
fun $alts:matchAlt* | _ => no_error_if_unused% throwUnsupportedSyntax)
|
| 65 |
+
else if catName == `tactic || catName == `conv then
|
| 66 |
+
`($[$doc?:docComment]? @[$(β mkAttrs `tactic),*]
|
| 67 |
+
aux_def elabRules $(mkIdent k) : Lean.Elab.Tactic.Tactic :=
|
| 68 |
+
fun $alts:matchAlt* | _ => no_error_if_unused% throwUnsupportedSyntax)
|
| 69 |
+
else
|
| 70 |
+
-- We considered making the command extensible and support new user-defined categories. We think it is unnecessary.
|
| 71 |
+
-- If users want this feature, they add their own `elab_rules` macro that uses this one as a fallback.
|
| 72 |
+
throwError "unsupported syntax category '{catName}'"
|
| 73 |
+
|
| 74 |
+
@[builtin_command_elab Β«elab_rulesΒ»] def elabElabRules : CommandElab :=
|
| 75 |
+
adaptExpander fun stx => match stx with
|
| 76 |
+
| `($[$doc?:docComment]? $[@[$attrs?,*]]? $attrKind:attrKind elab_rules $[: $cat?]? $[<= $expty?]? $alts:matchAlt*) =>
|
| 77 |
+
expandNoKindMacroRulesAux alts "elab_rules" fun kind? alts =>
|
| 78 |
+
`($[$doc?:docComment]? $[@[$attrs?,*]]? $attrKind:attrKind elab_rules $[(kind := $(mkIdent <$> kind?))]? $[: $cat?]? $[<= $expty?]? $alts:matchAlt*)
|
| 79 |
+
| `($[$doc?:docComment]? $[@[$attrs?,*]]? $attrKind:attrKind elab_rules (kind := $kind) $[: $cat?]? $[<= $expty?]? $alts:matchAlt*) =>
|
| 80 |
+
do elabElabRulesAux doc? attrs? attrKind (β resolveSyntaxKind kind.getId) cat? expty? alts
|
| 81 |
+
| _ => throwUnsupportedSyntax
|
| 82 |
+
|
| 83 |
+
@[builtin_command_elab Lean.Parser.Command.elab]
|
| 84 |
+
def elabElab : CommandElab
|
| 85 |
+
| `($[$doc?:docComment]? $[@[$attrs?,*]]? $attrKind:attrKind
|
| 86 |
+
elab%$tk$[:$prec?]? $[(name := $name?)]? $[(priority := $prio?)]? $args:macroArg* :
|
| 87 |
+
$cat $[<= $expectedType?]? => $rhs) => do
|
| 88 |
+
let prio β liftMacroM <| evalOptPrio prio?
|
| 89 |
+
let (stxParts, patArgs) := (β args.mapM expandMacroArg).unzip
|
| 90 |
+
-- name
|
| 91 |
+
let name β match name? with
|
| 92 |
+
| some name => pure name.getId
|
| 93 |
+
| none => addMacroScopeIfLocal (β liftMacroM <| mkNameFromParserSyntax cat.getId (mkNullNode stxParts)) attrKind
|
| 94 |
+
let nameId := name?.getD (mkIdentFrom tk name (canonical := true))
|
| 95 |
+
let pat := β¨mkNode ((β getCurrNamespace) ++ name) patArgsβ©
|
| 96 |
+
elabCommand <|β `(
|
| 97 |
+
$[$doc?:docComment]? $[@[$attrs?,*]]? $attrKind:attrKind
|
| 98 |
+
syntax%$tk$[:$prec?]? (name := $nameId) (priority := $(quote prio):num) $[$stxParts]* : $cat
|
| 99 |
+
$[$doc?:docComment]? elab_rules : $cat $[<= $expectedType?]? | `($pat) => $rhs)
|
| 100 |
+
| _ => throwUnsupportedSyntax
|
| 101 |
+
|
| 102 |
+
end Lean.Elab.Command
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/ErrorExplanation.lean
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Joseph Rotella
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.ErrorExplanation
|
| 8 |
+
import Lean.Meta.Eval
|
| 9 |
+
import Lean.Elab.Term
|
| 10 |
+
import Lean.Elab.Command
|
| 11 |
+
import Lean.Widget.UserWidget
|
| 12 |
+
|
| 13 |
+
namespace Lean.Elab.ErrorExplanation
|
| 14 |
+
|
| 15 |
+
open Meta Parser Term
|
| 16 |
+
|
| 17 |
+
-- We cannot import the definitions needed for this attribute in `Lean.Log`, so we instead add it
|
| 18 |
+
-- here
|
| 19 |
+
attribute [builtin_widget_module] Lean.errorDescriptionWidget
|
| 20 |
+
|
| 21 |
+
def expandNamedErrorMacro : Macro
|
| 22 |
+
| `(throwNamedErrorMacro| throwNamedError $id:ident $msg:interpolatedStr) =>
|
| 23 |
+
``(Lean.throwNamedError $(quote id.getId) m! $msg)
|
| 24 |
+
| `(throwNamedErrorMacro| throwNamedError $id $msg:term) =>
|
| 25 |
+
``(Lean.throwNamedError $(quote id.getId) $msg)
|
| 26 |
+
| `(throwNamedErrorAtMacro| throwNamedErrorAt $ref $id $msg:interpolatedStr) =>
|
| 27 |
+
``(Lean.throwNamedErrorAt $ref $(quote id.getId) m! $msg)
|
| 28 |
+
| `(throwNamedErrorAtMacro| throwNamedErrorAt $ref $id $msg:term) =>
|
| 29 |
+
``(Lean.throwNamedErrorAt $ref $(quote id.getId) $msg)
|
| 30 |
+
| `(logNamedErrorMacro| logNamedError $id $msg:interpolatedStr) =>
|
| 31 |
+
``(Lean.logNamedError $(quote id.getId) m! $msg)
|
| 32 |
+
| `(logNamedErrorMacro| logNamedError $id $msg:term) =>
|
| 33 |
+
``(Lean.logNamedError $(quote id.getId) $msg)
|
| 34 |
+
| `(logNamedErrorAtMacro| logNamedErrorAt $ref $id $msg:interpolatedStr) =>
|
| 35 |
+
``(Lean.logNamedErrorAt $ref $(quote id.getId) m! $msg)
|
| 36 |
+
| `(logNamedErrorAtMacro| logNamedErrorAt $ref $id $msg:term) =>
|
| 37 |
+
``(Lean.logNamedErrorAt $ref $(quote id.getId) $msg)
|
| 38 |
+
| `(logNamedWarningMacro| logNamedWarning $id $msg:interpolatedStr) =>
|
| 39 |
+
``(Lean.logNamedWarning $(quote id.getId) m! $msg)
|
| 40 |
+
| `(logNamedWarningMacro| logNamedWarning $id $msg:term) =>
|
| 41 |
+
``(Lean.logNamedWarning $(quote id.getId) $msg)
|
| 42 |
+
| `(logNamedWarningAtMacro| logNamedWarningAt $ref $id $msg:interpolatedStr) =>
|
| 43 |
+
``(Lean.logNamedWarningAt $ref $(quote id.getId) m! $msg)
|
| 44 |
+
| `(logNamedWarningAtMacro| logNamedWarningAt $ref $id $msg:term) =>
|
| 45 |
+
``(Lean.logNamedWarningAt $ref $(quote id.getId) $msg)
|
| 46 |
+
| _ => Macro.throwUnsupported
|
| 47 |
+
|
| 48 |
+
/--
|
| 49 |
+
Maps macro syntax categories to a pair of the module containing the declaration on which the macro
|
| 50 |
+
depends and the name of that declaration.
|
| 51 |
+
-/
|
| 52 |
+
private def macroDeclMap :=
|
| 53 |
+
Std.HashMap.ofList
|
| 54 |
+
[(``throwNamedErrorMacro, (`Lean.Exception, ``Lean.throwNamedError)),
|
| 55 |
+
(``throwNamedErrorAtMacro, (`Lean.Exception, ``Lean.throwNamedErrorAt)),
|
| 56 |
+
(``logNamedErrorMacro, (`Lean.Log, ``Lean.logNamedError)),
|
| 57 |
+
(``logNamedErrorAtMacro, (`Lean.Log, ``Lean.logNamedErrorAt)),
|
| 58 |
+
(``logNamedWarningMacro, (`Lean.Log, ``Lean.logNamedWarning)),
|
| 59 |
+
(``logNamedWarningAtMacro, (`Lean.Log, ``Lean.logNamedWarningAt))]
|
| 60 |
+
|
| 61 |
+
@[builtin_term_elab throwNamedErrorMacro, builtin_term_elab throwNamedErrorAtMacro,
|
| 62 |
+
builtin_term_elab logNamedErrorMacro, builtin_term_elab logNamedErrorAtMacro,
|
| 63 |
+
builtin_term_elab logNamedWarningMacro, builtin_term_elab logNamedWarningAtMacro]
|
| 64 |
+
def elabCheckedNamedError : TermElab := fun stx expType? => do
|
| 65 |
+
if let some (module, decl) := macroDeclMap.get? stx.getKind then
|
| 66 |
+
if !(β getEnv).contains decl then
|
| 67 |
+
throwError m!"The constant `{decl}` has not been imported" ++
|
| 68 |
+
.hint' m!"Add `import {module}` to this file's header to use this macro"
|
| 69 |
+
let (id, numArgsExpected) :=
|
| 70 |
+
if stx.isOfKind ``throwNamedErrorAtMacro || stx.isOfKind ``logNamedErrorAtMacro
|
| 71 |
+
|| stx.isOfKind ``logNamedWarningAtMacro then
|
| 72 |
+
(stx[2], 5)
|
| 73 |
+
else
|
| 74 |
+
(stx[1], 4)
|
| 75 |
+
-- Remove the message term from the span. If we have a trailing `.`, we fail to parse the message
|
| 76 |
+
-- term and so leave `stx` unchanged. The in-progress identifier will always be the penultimate
|
| 77 |
+
-- argument of `span`.
|
| 78 |
+
let span := if stx.getNumArgs == numArgsExpected then
|
| 79 |
+
stx.setArgs (stx.getArgs[*...(stx.getNumArgs - 1)])
|
| 80 |
+
else
|
| 81 |
+
stx
|
| 82 |
+
let partialId := span[span.getNumArgs - 2]
|
| 83 |
+
addCompletionInfo <| CompletionInfo.errorName span partialId
|
| 84 |
+
let name := id.getId.eraseMacroScopes
|
| 85 |
+
pushInfoLeaf <| .ofErrorNameInfo { stx := id, errorName := name }
|
| 86 |
+
if let some explan := getErrorExplanationRaw? (β getEnv) name then
|
| 87 |
+
if let some removedVersion := explan.metadata.removedVersion? then
|
| 88 |
+
logWarningAt id m!"The error name `{name}` was removed in Lean version {removedVersion} and \
|
| 89 |
+
should not be used."
|
| 90 |
+
else
|
| 91 |
+
logErrorAt id m!"There is no explanation associated with the name `{name}`. \
|
| 92 |
+
Add an explanation of this error to the `Lean.ErrorExplanations` module."
|
| 93 |
+
let stx' β liftMacroM <| expandNamedErrorMacro stx
|
| 94 |
+
elabTerm stx' expType?
|
| 95 |
+
|
| 96 |
+
open Command in
|
| 97 |
+
@[builtin_command_elab registerErrorExplanationStx] def elabRegisterErrorExplanation : CommandElab
|
| 98 |
+
| `(registerErrorExplanationStx| $docStx:docComment register_error_explanation%$cmd $id:ident $t:term) => withRef cmd do
|
| 99 |
+
unless (β getEnv).contains ``Lean.ErrorExplanation do
|
| 100 |
+
throwError "To use this command, add `import Lean.ErrorExplanation` to the header of this file"
|
| 101 |
+
let tp := mkConst ``ErrorExplanation.Metadata
|
| 102 |
+
let metadata β runTermElabM <| fun _ => unsafe do
|
| 103 |
+
let e β elabTerm t tp
|
| 104 |
+
if e.hasSyntheticSorry then throwAbortTerm
|
| 105 |
+
evalExpr ErrorExplanation.Metadata tp e
|
| 106 |
+
let name := id.getId
|
| 107 |
+
if name.isAnonymous then
|
| 108 |
+
throwErrorAt id "Invalid name for error explanation: `{id}`"
|
| 109 |
+
if name.hasMacroScopes then
|
| 110 |
+
-- Use `id` rather than `name` for nicer rendering
|
| 111 |
+
throwErrorAt id m!"Invalid name `{id}`: Error explanations cannot have inaccessible names. \
|
| 112 |
+
This error often occurs when an error explanation is generated using a macro."
|
| 113 |
+
if name.getNumParts != 2 then
|
| 114 |
+
throwErrorAt id m!"Invalid name `{name}`: Error explanation names must have two components"
|
| 115 |
+
++ .note m!"The first component of an error explanation name identifies the package from \
|
| 116 |
+
which the error originates, and the second identifies the error itself."
|
| 117 |
+
validateDocComment docStx
|
| 118 |
+
let doc β getDocStringText docStx
|
| 119 |
+
if errorExplanationExt.getState (β getEnv) |>.contains name then
|
| 120 |
+
throwErrorAt id m!"Cannot add explanation: An error explanation already exists for `{name}`"
|
| 121 |
+
if let .error (lineOffset, msg) := ErrorExplanation.processDoc doc then
|
| 122 |
+
let some range := docStx.raw[1].getRange? | throwError msg
|
| 123 |
+
let fileMap β getFileMap
|
| 124 |
+
let β¨startLine, _β© := fileMap.toPosition range.start
|
| 125 |
+
let errLine := startLine + lineOffset
|
| 126 |
+
let synth := Syntax.ofRange { start := fileMap.ofPosition β¨errLine, 0β©,
|
| 127 |
+
stop := fileMap.ofPosition β¨errLine + 1, 0β© }
|
| 128 |
+
throwErrorAt synth msg
|
| 129 |
+
let (declLoc? : Option DeclarationLocation) β do
|
| 130 |
+
let map β getFileMap
|
| 131 |
+
let start := id.raw.getPos?.getD 0
|
| 132 |
+
let fin := id.raw.getTailPos?.getD start
|
| 133 |
+
pure <| some {
|
| 134 |
+
module := (β getMainModule)
|
| 135 |
+
range := .ofStringPositions map start fin
|
| 136 |
+
}
|
| 137 |
+
modifyEnv (errorExplanationExt.addEntry Β· (name, { metadata, doc, declLoc? }))
|
| 138 |
+
| _ => throwUnsupportedSyntax
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Eval.lean
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Sebastian Ullrich. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Eval
|
| 8 |
+
import Lean.Elab.SyntheticMVars
|
| 9 |
+
|
| 10 |
+
namespace Lean.Elab.Term
|
| 11 |
+
open Meta
|
| 12 |
+
|
| 13 |
+
unsafe def evalTerm (Ξ±) (type : Expr) (value : Syntax) (safety := DefinitionSafety.safe) : TermElabM Ξ± := withoutModifyingEnv do
|
| 14 |
+
let v β elabTermEnsuringType value type
|
| 15 |
+
synthesizeSyntheticMVarsNoPostponing
|
| 16 |
+
let v β instantiateMVars v
|
| 17 |
+
if (β logUnassignedUsingErrorInfos (β getMVars v)) then throwAbortTerm
|
| 18 |
+
evalExpr Ξ± type v safety
|
| 19 |
+
|
| 20 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Exception.lean
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.InternalExceptionId
|
| 8 |
+
import Lean.Exception
|
| 9 |
+
|
| 10 |
+
namespace Lean.Elab
|
| 11 |
+
|
| 12 |
+
builtin_initialize postponeExceptionId : InternalExceptionId β registerInternalExceptionId `postpone
|
| 13 |
+
builtin_initialize unsupportedSyntaxExceptionId : InternalExceptionId β registerInternalExceptionId `unsupportedSyntax
|
| 14 |
+
builtin_initialize abortCommandExceptionId : InternalExceptionId β registerInternalExceptionId `abortCommandElab
|
| 15 |
+
builtin_initialize abortTermExceptionId : InternalExceptionId β registerInternalExceptionId `abortTermElab
|
| 16 |
+
builtin_initialize abortTacticExceptionId : InternalExceptionId β registerInternalExceptionId `abortTactic
|
| 17 |
+
builtin_initialize autoBoundImplicitExceptionId : InternalExceptionId β registerInternalExceptionId `autoBoundImplicit
|
| 18 |
+
|
| 19 |
+
def throwPostpone [MonadExceptOf Exception m] : m Ξ± :=
|
| 20 |
+
throw $ Exception.internal postponeExceptionId
|
| 21 |
+
|
| 22 |
+
def throwUnsupportedSyntax [MonadExceptOf Exception m] : m Ξ± :=
|
| 23 |
+
throw $ Exception.internal unsupportedSyntaxExceptionId
|
| 24 |
+
|
| 25 |
+
def throwIllFormedSyntax [Monad m] [MonadError m] : m Ξ± :=
|
| 26 |
+
throwError "ill-formed syntax"
|
| 27 |
+
|
| 28 |
+
def throwAutoBoundImplicitLocal [MonadExceptOf Exception m] (n : Name) : m Ξ± :=
|
| 29 |
+
throw $ Exception.internal autoBoundImplicitExceptionId <| KVMap.empty.insert `localId n
|
| 30 |
+
|
| 31 |
+
def isAutoBoundImplicitLocalException? (ex : Exception) : Option Name :=
|
| 32 |
+
match ex with
|
| 33 |
+
| Exception.internal id k =>
|
| 34 |
+
if id == autoBoundImplicitExceptionId then
|
| 35 |
+
some <| k.getName `localId `x
|
| 36 |
+
else
|
| 37 |
+
none
|
| 38 |
+
| _ => none
|
| 39 |
+
|
| 40 |
+
def throwAlreadyDeclaredUniverseLevel [Monad m] [MonadError m] (u : Name) : m Ξ± :=
|
| 41 |
+
throwError "a universe level named '{u}' has already been declared"
|
| 42 |
+
|
| 43 |
+
-- Throw exception to abort elaboration of the current command without producing any error message
|
| 44 |
+
def throwAbortCommand {Ξ± m} [MonadExcept Exception m] : m Ξ± :=
|
| 45 |
+
throw <| Exception.internal abortCommandExceptionId
|
| 46 |
+
|
| 47 |
+
-- Throw exception to abort elaboration of the current term without producing any error message
|
| 48 |
+
def throwAbortTerm {Ξ± m} [MonadExcept Exception m] : m Ξ± :=
|
| 49 |
+
throw <| Exception.internal abortTermExceptionId
|
| 50 |
+
|
| 51 |
+
-- Throw exception to abort evaluation of the current tactic without producing any error message
|
| 52 |
+
def throwAbortTactic {Ξ± m} [MonadExcept Exception m] : m Ξ± :=
|
| 53 |
+
throw <| Exception.internal abortTacticExceptionId
|
| 54 |
+
|
| 55 |
+
def isAbortTacticException (ex : Exception) : Bool :=
|
| 56 |
+
match ex with
|
| 57 |
+
| Exception.internal id .. => id == abortTacticExceptionId
|
| 58 |
+
| _ => false
|
| 59 |
+
|
| 60 |
+
def isAbortExceptionId (id : InternalExceptionId) : Bool :=
|
| 61 |
+
id == abortCommandExceptionId || id == abortTermExceptionId || id == abortTacticExceptionId
|
| 62 |
+
|
| 63 |
+
def mkMessageCore (fileName : String) (fileMap : FileMap) (data : MessageData) (severity : MessageSeverity) (pos : String.Pos) (endPos : String.Pos) : Message :=
|
| 64 |
+
let pos := fileMap.toPosition pos
|
| 65 |
+
let endPos := fileMap.toPosition endPos
|
| 66 |
+
{ fileName, pos, endPos, data, severity }
|
| 67 |
+
|
| 68 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Extra.lean
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Kyle Miller, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.App
|
| 8 |
+
import Lean.Elab.BuiltinNotation
|
| 9 |
+
|
| 10 |
+
/-! # Auxiliary elaboration functions: AKA custom elaborators -/
|
| 11 |
+
|
| 12 |
+
namespace Lean.Elab.Term
|
| 13 |
+
open Meta
|
| 14 |
+
|
| 15 |
+
private def getMonadForIn (expectedType? : Option Expr) : TermElabM Expr := do
|
| 16 |
+
match expectedType? with
|
| 17 |
+
| none => throwError "invalid 'for_in%' notation, expected type is not available"
|
| 18 |
+
| some expectedType =>
|
| 19 |
+
match (β isTypeApp? expectedType) with
|
| 20 |
+
| some (m, _) => return m
|
| 21 |
+
| none => throwError "invalid 'for_in%' notation, expected type is not of the form `M Ξ±`{indentExpr expectedType}"
|
| 22 |
+
|
| 23 |
+
private def throwForInFailure (forInInstance : Expr) : TermElabM Expr :=
|
| 24 |
+
throwError "failed to synthesize instance for 'for_in%' notation{indentExpr forInInstance}"
|
| 25 |
+
|
| 26 |
+
@[builtin_term_elab forInMacro] def elabForIn : TermElab := fun stx expectedType? => do
|
| 27 |
+
match stx with
|
| 28 |
+
| `(for_in% $col $init $body) =>
|
| 29 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 30 |
+
let colE β elabTerm col none
|
| 31 |
+
let m β getMonadForIn expectedType?
|
| 32 |
+
let colType β inferType colE
|
| 33 |
+
let elemType β mkFreshExprMVar (mkSort (mkLevelSucc (β mkFreshLevelMVar)))
|
| 34 |
+
let forInInstance β try
|
| 35 |
+
mkAppM ``ForIn #[m, colType, elemType]
|
| 36 |
+
catch _ =>
|
| 37 |
+
tryPostpone; throwError "failed to construct 'ForIn' instance for collection{indentExpr colType}\nand monad{indentExpr m}"
|
| 38 |
+
match (β trySynthInstance forInInstance) with
|
| 39 |
+
| .some inst =>
|
| 40 |
+
let forInFn β mkConst ``forIn
|
| 41 |
+
elabAppArgs forInFn
|
| 42 |
+
(namedArgs := #[{ name := `m, val := Arg.expr m}, { name := `Ξ±, val := Arg.expr elemType }, { name := `self, val := Arg.expr inst }])
|
| 43 |
+
(args := #[Arg.expr colE, Arg.stx init, Arg.stx body])
|
| 44 |
+
(expectedType? := expectedType?)
|
| 45 |
+
(explicit := false) (ellipsis := false) (resultIsOutParamSupport := false)
|
| 46 |
+
| .undef => tryPostpone; throwForInFailure forInInstance
|
| 47 |
+
| .none => throwForInFailure forInInstance
|
| 48 |
+
| _ => throwUnsupportedSyntax
|
| 49 |
+
|
| 50 |
+
@[builtin_term_elab forInMacro'] def elabForIn' : TermElab := fun stx expectedType? => do
|
| 51 |
+
match stx with
|
| 52 |
+
| `(for_in'% $col $init $body) =>
|
| 53 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 54 |
+
let colE β elabTerm col none
|
| 55 |
+
let m β getMonadForIn expectedType?
|
| 56 |
+
let colType β inferType colE
|
| 57 |
+
let elemType β mkFreshExprMVar (mkSort (mkLevelSucc (β mkFreshLevelMVar)))
|
| 58 |
+
let forInInstance β
|
| 59 |
+
try
|
| 60 |
+
let memType β mkFreshExprMVar (β mkAppM ``Membership #[elemType, colType])
|
| 61 |
+
mkAppM ``ForIn' #[m, colType, elemType, memType]
|
| 62 |
+
catch _ =>
|
| 63 |
+
tryPostpone; throwError "failed to construct `ForIn'` instance for collection{indentExpr colType}\nand monad{indentExpr m}"
|
| 64 |
+
match (β trySynthInstance forInInstance) with
|
| 65 |
+
| .some inst =>
|
| 66 |
+
let forInFn β mkConst ``forIn'
|
| 67 |
+
elabAppArgs forInFn
|
| 68 |
+
(namedArgs := #[{ name := `m, val := Arg.expr m}, { name := `Ξ±, val := Arg.expr elemType}, { name := `self, val := Arg.expr inst }])
|
| 69 |
+
(args := #[Arg.expr colE, Arg.stx init, Arg.stx body])
|
| 70 |
+
(expectedType? := expectedType?)
|
| 71 |
+
(explicit := false) (ellipsis := false) (resultIsOutParamSupport := false)
|
| 72 |
+
| .undef => tryPostpone; throwForInFailure forInInstance
|
| 73 |
+
| .none => throwForInFailure forInInstance
|
| 74 |
+
| _ => throwUnsupportedSyntax
|
| 75 |
+
|
| 76 |
+
namespace Op
|
| 77 |
+
/-!
|
| 78 |
+
|
| 79 |
+
The elaborator for expression trees of `binop%`, `binop_lazy%`, `leftact%`, `rightact%`, and `unop%` terms.
|
| 80 |
+
|
| 81 |
+
At a high level, the elaborator tries to solve for a type that each of the operands in the expression tree
|
| 82 |
+
can be coerced to, while taking into account the expected type for the entire expression tree.
|
| 83 |
+
Once this type is computed (and if it exists), it inserts coercions where needed.
|
| 84 |
+
|
| 85 |
+
Here are brief descriptions of each of the operator types:
|
| 86 |
+
- `binop% f a b` elaborates `f a b` as a binary operator with two operands `a` and `b`,
|
| 87 |
+
and each operand participates in the protocol.
|
| 88 |
+
- `binop_lazy% f a b` is like `binop%` but elaborates as `f a (fun () => b)`.
|
| 89 |
+
- `unop% f a` elaborates `f a` as a unary operator with one operand `a`, which participates in the protocol.
|
| 90 |
+
- `leftact% f a b` elaborates `f a b` as a left action (the `a` operand "acts upon" the `b` operand).
|
| 91 |
+
Only `b` participates in the protocol since `a` can have an unrelated type, for example scalar multiplication of vectors.
|
| 92 |
+
- `rightact% f a b` elaborates `f a b` as a right action (the `b` operand "acts upon" the `a` operand).
|
| 93 |
+
Only `a` participates in the protocol since `b` can have an unrelated type.
|
| 94 |
+
This is used by `HPow` since, for example, there are both `Real -> Nat -> Real` and `Real -> Real -> Real`
|
| 95 |
+
exponentiation functions, and we prefer the former in the case of `x ^ 2`, but `binop%` would choose the latter. (#2854)
|
| 96 |
+
- There are also `binrel%` and `binrel_no_prop%` (see the docstring for `elabBinRelCore`).
|
| 97 |
+
|
| 98 |
+
The elaborator works as follows:
|
| 99 |
+
|
| 100 |
+
1- Expand macros.
|
| 101 |
+
2- Convert `Syntax` object corresponding to the `binop%/...` term into a `Tree`.
|
| 102 |
+
The `toTree` method visits nested `binop%/...` terms and parentheses.
|
| 103 |
+
3- Synthesize pending metavariables without applying default instances and using the
|
| 104 |
+
`(mayPostpone := true)`.
|
| 105 |
+
4- Tries to compute a maximal type for the tree computed at step 2.
|
| 106 |
+
We say a type Ξ± is smaller than type Ξ² if there is a (nondependent) coercion from Ξ± to Ξ².
|
| 107 |
+
We are currently ignoring the case we may have cycles in the coercion graph.
|
| 108 |
+
If there are "uncomparable" types Ξ± and Ξ² in the tree, we skip the next step.
|
| 109 |
+
We say two types are "uncomparable" if there isn't a coercion between them.
|
| 110 |
+
Note that two types may be "uncomparable" because some typing information may still be missing.
|
| 111 |
+
5- We traverse the tree and inject coercions to the "maximal" type when needed.
|
| 112 |
+
|
| 113 |
+
Recall that the coercions are expanded eagerly by the elaborator.
|
| 114 |
+
|
| 115 |
+
Properties:
|
| 116 |
+
|
| 117 |
+
a) Given `n : Nat` and `i : Nat`, it can successfully elaborate `n + i` and `i + n`. Recall that Lean 3
|
| 118 |
+
fails on the former.
|
| 119 |
+
|
| 120 |
+
b) The coercions are inserted in the "leaves" like in Lean 3.
|
| 121 |
+
|
| 122 |
+
c) There are no coercions "hidden" inside instances, and we can elaborate
|
| 123 |
+
```
|
| 124 |
+
axiom Int.add_comm (i j : Int) : i + j = j + i
|
| 125 |
+
|
| 126 |
+
example (n : Nat) (i : Int) : n + i = i + n := by
|
| 127 |
+
rw [Int.add_comm]
|
| 128 |
+
```
|
| 129 |
+
Recall that the `rw` tactic used to fail because our old `binop%` elaborator would hide
|
| 130 |
+
coercions inside of a `HAdd` instance.
|
| 131 |
+
|
| 132 |
+
Remarks:
|
| 133 |
+
|
| 134 |
+
* In the new `binop%` and related elaborators the decision whether a coercion will be inserted or not
|
| 135 |
+
is made at `binop%` elaboration time. This was not the case in the old elaborator.
|
| 136 |
+
For example, an instance, such as `HAdd Int ?m ?n`, could be created when executing
|
| 137 |
+
the `binop%` elaborator, and only resolved much later. We try to minimize this problem
|
| 138 |
+
by synthesizing pending metavariables at step 3.
|
| 139 |
+
|
| 140 |
+
* For types containing heterogeneous operators (e.g., matrix multiplication), step 4 will fail
|
| 141 |
+
and we will skip coercion insertion. For example, `x : Matrix Real 5 4` and `y : Matrix Real 4 8`,
|
| 142 |
+
there is no coercion `Matrix Real 5 4` from `Matrix Real 4 8` and vice-versa, but
|
| 143 |
+
`x * y` is elaborated successfully and has type `Matrix Real 5 8`.
|
| 144 |
+
|
| 145 |
+
* The `leftact%` and `rightact%` elaborators are to handle binary operations where only one of
|
| 146 |
+
the arguments participates in the protocol. For example, in `2 ^ n + y` with `n : Nat` and `y : Real`,
|
| 147 |
+
we do not want to coerce `n` to be a real as well, but we do want to elaborate `2 : Real`.
|
| 148 |
+
-/
|
| 149 |
+
|
| 150 |
+
private inductive BinOpKind where
|
| 151 |
+
| regular -- `binop%`
|
| 152 |
+
| lazy -- `binop_lazy%`
|
| 153 |
+
| leftact -- `leftact%`
|
| 154 |
+
| rightact -- `rightact%`
|
| 155 |
+
deriving BEq
|
| 156 |
+
|
| 157 |
+
private inductive Tree where
|
| 158 |
+
/--
|
| 159 |
+
Leaf of the tree.
|
| 160 |
+
We store the `infoTrees` generated when elaborating `val`. These trees become
|
| 161 |
+
subtrees of the infotree nodes generated for `op` nodes.
|
| 162 |
+
-/
|
| 163 |
+
| term (ref : Syntax) (infoTrees : PersistentArray InfoTree) (val : Expr)
|
| 164 |
+
/--
|
| 165 |
+
`ref` is the original syntax that expanded into `binop%/...`.
|
| 166 |
+
-/
|
| 167 |
+
| binop (ref : Syntax) (kind : BinOpKind) (f : Expr) (lhs rhs : Tree)
|
| 168 |
+
/--
|
| 169 |
+
`ref` is the original syntax that expanded into `unop%`.
|
| 170 |
+
-/
|
| 171 |
+
| unop (ref : Syntax) (f : Expr) (arg : Tree)
|
| 172 |
+
/--
|
| 173 |
+
Used for assembling the info tree. We store this information
|
| 174 |
+
to make sure "go to definition" behaves similarly to notation defined without using `binop%` helper elaborator.
|
| 175 |
+
-/
|
| 176 |
+
| macroExpansion (macroName : Name) (stx stx' : Syntax) (nested : Tree)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
private partial def toTree (s : Syntax) : TermElabM Tree := do
|
| 180 |
+
/-
|
| 181 |
+
Remark: ew used to use `expandMacros` here, but this is a bad idiom
|
| 182 |
+
because we do not record the macro expansion information in the info tree.
|
| 183 |
+
We now manually expand the notation in the `go` function, and save
|
| 184 |
+
the macro declaration names in the `op` nodes.
|
| 185 |
+
-/
|
| 186 |
+
let result β go s
|
| 187 |
+
synthesizeSyntheticMVars (postpone := .yes)
|
| 188 |
+
return result
|
| 189 |
+
where
|
| 190 |
+
go (s : Syntax) := do
|
| 191 |
+
match s with
|
| 192 |
+
| `(binop% $f $lhs $rhs) => processBinOp s .regular f lhs rhs
|
| 193 |
+
| `(binop_lazy% $f $lhs $rhs) => processBinOp s .lazy f lhs rhs
|
| 194 |
+
| `(unop% $f $arg) => processUnOp s f arg
|
| 195 |
+
| `(leftact% $f $lhs $rhs) => processBinOp s .leftact f lhs rhs
|
| 196 |
+
| `(rightact% $f $lhs $rhs) => processBinOp s .rightact f lhs rhs
|
| 197 |
+
| `(($e)) =>
|
| 198 |
+
if hasCDot e then
|
| 199 |
+
processLeaf s
|
| 200 |
+
else
|
| 201 |
+
go e
|
| 202 |
+
| _ =>
|
| 203 |
+
withRef s do
|
| 204 |
+
match (β liftMacroM <| expandMacroImpl? (β getEnv) s) with
|
| 205 |
+
| some (macroName, s?) =>
|
| 206 |
+
let s' β liftMacroM <| liftExcept s?
|
| 207 |
+
withPushMacroExpansionStack s s' do
|
| 208 |
+
return .macroExpansion macroName s s' (β go s')
|
| 209 |
+
| none => processLeaf s
|
| 210 |
+
|
| 211 |
+
processBinOp (ref : Syntax) (kind : BinOpKind) (f lhs rhs : Syntax) := do
|
| 212 |
+
let some f οΏ½οΏ½ resolveId? f | throwUnknownConstantAt f f.getId
|
| 213 |
+
-- treat corresponding argument as leaf for `leftact/rightact`
|
| 214 |
+
let lhs β if kind == .leftact then processLeaf lhs else go lhs
|
| 215 |
+
let rhs β if kind == .rightact then processLeaf rhs else go rhs
|
| 216 |
+
return .binop ref kind f lhs rhs
|
| 217 |
+
|
| 218 |
+
processUnOp (ref : Syntax) (f arg : Syntax) := do
|
| 219 |
+
let some f β resolveId? f | throwUnknownConstantAt f f.getId
|
| 220 |
+
return .unop ref f (β go arg)
|
| 221 |
+
|
| 222 |
+
processLeaf (s : Syntax) := do
|
| 223 |
+
let e β elabTerm s none
|
| 224 |
+
let info β getResetInfoTrees
|
| 225 |
+
return .term s info e
|
| 226 |
+
|
| 227 |
+
-- Auxiliary function used at `analyze`
|
| 228 |
+
private def hasCoe (fromType toType : Expr) : TermElabM Bool := do
|
| 229 |
+
if (β getEnv).contains ``CoeT then
|
| 230 |
+
withLocalDeclD `x fromType fun x => do
|
| 231 |
+
match β coerceSimple? x toType with
|
| 232 |
+
| .some _ => return true
|
| 233 |
+
| .none => return false
|
| 234 |
+
| .undef => return false -- TODO: should we do something smarter here?
|
| 235 |
+
else
|
| 236 |
+
return false
|
| 237 |
+
|
| 238 |
+
private structure AnalyzeResult where
|
| 239 |
+
max? : Option Expr := none
|
| 240 |
+
/-- `true` if there are two types `Ξ±` and `Ξ²` where we don't have coercions in any direction. -/
|
| 241 |
+
hasUncomparable : Bool := false
|
| 242 |
+
/-- `true` if there are any leaf terms with an unknown type (according to `isUnknown`). -/
|
| 243 |
+
hasUnknown : Bool := false
|
| 244 |
+
|
| 245 |
+
private def isUnknown : Expr β Bool
|
| 246 |
+
| .mvar .. => true
|
| 247 |
+
| .app f _ => isUnknown f
|
| 248 |
+
| .letE _ _ _ b _ => isUnknown b
|
| 249 |
+
| .mdata _ b => isUnknown b
|
| 250 |
+
| _ => false
|
| 251 |
+
|
| 252 |
+
private def analyze (t : Tree) (expectedType? : Option Expr) : TermElabM AnalyzeResult := do
|
| 253 |
+
let max? β
|
| 254 |
+
match expectedType? with
|
| 255 |
+
| none => pure none
|
| 256 |
+
| some expectedType =>
|
| 257 |
+
let expectedType := (β instantiateMVars expectedType).cleanupAnnotations
|
| 258 |
+
if isUnknown expectedType then pure none else pure (some expectedType)
|
| 259 |
+
(go t *> get).run' { max? }
|
| 260 |
+
where
|
| 261 |
+
go (t : Tree) : StateRefT AnalyzeResult TermElabM Unit := do
|
| 262 |
+
unless (β get).hasUncomparable do
|
| 263 |
+
match t with
|
| 264 |
+
| .macroExpansion _ _ _ nested => go nested
|
| 265 |
+
| .binop _ .leftact _ _ rhs => go rhs
|
| 266 |
+
| .binop _ .rightact _ lhs _ => go lhs
|
| 267 |
+
| .binop _ _ _ lhs rhs => go lhs; go rhs
|
| 268 |
+
| .unop _ _ arg => go arg
|
| 269 |
+
| .term _ _ val =>
|
| 270 |
+
let type := (β instantiateMVars (β inferType val)).cleanupAnnotations
|
| 271 |
+
if isUnknown type then
|
| 272 |
+
modify fun s => { s with hasUnknown := true }
|
| 273 |
+
else
|
| 274 |
+
match (β get).max? with
|
| 275 |
+
| none => modify fun s => { s with max? := type }
|
| 276 |
+
| some max =>
|
| 277 |
+
/-
|
| 278 |
+
Remark: Previously, we used `withNewMCtxDepth` to prevent metavariables in `max` and `type` from being assigned.
|
| 279 |
+
|
| 280 |
+
Reason: This is a heuristic procedure for introducing coercions in scenarios such as:
|
| 281 |
+
- Given `(n : Nat) (i : Int)`, elaborate `n = i`. The coercion must be inserted at `n`.
|
| 282 |
+
Consider the elaboration problem `(n + 0) + i`, where the type of term `0` is a metavariable.
|
| 283 |
+
We do not want it to be elaborated as `(Int.ofNat n + Int.ofNat (0 : Nat)) + i`; instead, we prefer the result to be `(Int.ofNat n + (0 : Int)) + i`.
|
| 284 |
+
Here is another example where we avoid assigning metavariables: `max := BitVec n` and `type := BitVec ?m`.
|
| 285 |
+
|
| 286 |
+
However, the combination `withNewMCtxDepth <| isDefEqGuarded max type` introduced performance issues in several
|
| 287 |
+
Mathlib files because `isDefEq` was spending a lot of time unfolding definitions in `max` and `type` before failing.
|
| 288 |
+
|
| 289 |
+
To address this issue, we allowed only reducible definitions to be unfolded during this check, using
|
| 290 |
+
`withNewMCtxDepth <| withReducible <| isDefEqGuarded max type`. This change fixed some performance issues but created new ones.
|
| 291 |
+
Lean was now spending time trying to use `hasCoe`, likely occurring in places where `withNewMCtxDepth <| isDefEqGuarded max type`
|
| 292 |
+
used to succeed but was now failing after we introduced `withReducible`.
|
| 293 |
+
|
| 294 |
+
We then considered using just `isDefEqGuarded max type` and changing the definition of `isUnknown`. In the new definition,
|
| 295 |
+
the else-case would be `| e => e.hasExprMVar` instead of `| _ => false`. However, we could not even compile this repo using
|
| 296 |
+
this configuration. The problem arises because some files require coercions even when `max` contains metavariables,
|
| 297 |
+
for example: `max := Option ?m` and `type := Name`.
|
| 298 |
+
|
| 299 |
+
As a result, rather than restricting reducibility, we decided to set `Meta.Config.isDefEqStuckEx := true`.
|
| 300 |
+
This means that if `isDefEq` encounters a subproblem `?m =?= a` where `?m` is non-assignable, it aborts the test
|
| 301 |
+
instead of unfolding definitions.
|
| 302 |
+
-/
|
| 303 |
+
unless (β withNewMCtxDepth <| withConfig (fun config => { config with isDefEqStuckEx := true }) <| isDefEqGuarded max type) do
|
| 304 |
+
if (β hasCoe type max) then
|
| 305 |
+
return ()
|
| 306 |
+
else if (β hasCoe max type) then
|
| 307 |
+
modify fun s => { s with max? := type }
|
| 308 |
+
else
|
| 309 |
+
trace[Elab.binop] "uncomparable types: {max}, {type}"
|
| 310 |
+
modify fun s => { s with hasUncomparable := true }
|
| 311 |
+
|
| 312 |
+
private def mkBinOp (lazy : Bool) (f : Expr) (lhs rhs : Expr) : TermElabM Expr := do
|
| 313 |
+
let mut rhs := rhs
|
| 314 |
+
if lazy then
|
| 315 |
+
rhs β mkFunUnit rhs
|
| 316 |
+
elabAppArgs f #[] #[Arg.expr lhs, Arg.expr rhs] (expectedType? := none) (explicit := false) (ellipsis := false) (resultIsOutParamSupport := false)
|
| 317 |
+
|
| 318 |
+
private def mkUnOp (f : Expr) (arg : Expr) : TermElabM Expr := do
|
| 319 |
+
elabAppArgs f #[] #[Arg.expr arg] (expectedType? := none) (explicit := false) (ellipsis := false) (resultIsOutParamSupport := false)
|
| 320 |
+
|
| 321 |
+
private def toExprCore (t : Tree) : TermElabM Expr := do
|
| 322 |
+
match t with
|
| 323 |
+
| .term _ trees e =>
|
| 324 |
+
modifyInfoState (fun s => { s with trees := s.trees ++ trees }); return e
|
| 325 |
+
| .binop ref kind f lhs rhs =>
|
| 326 |
+
withRef ref <|
|
| 327 |
+
withTermInfoContext' .anonymous ref do
|
| 328 |
+
mkBinOp (kind == .lazy) f (β toExprCore lhs) (β toExprCore rhs)
|
| 329 |
+
| .unop ref f arg =>
|
| 330 |
+
withRef ref <|
|
| 331 |
+
withTermInfoContext' .anonymous ref do
|
| 332 |
+
mkUnOp f (β toExprCore arg)
|
| 333 |
+
| .macroExpansion macroName stx stx' nested =>
|
| 334 |
+
withRef stx <|
|
| 335 |
+
withTermInfoContext' macroName stx <|
|
| 336 |
+
withMacroExpansion stx stx' <|
|
| 337 |
+
toExprCore nested
|
| 338 |
+
|
| 339 |
+
/--
|
| 340 |
+
Auxiliary function to decide whether we should coerce `f`'s argument to `maxType` or not.
|
| 341 |
+
- `f` is a binary operator.
|
| 342 |
+
- `lhs == true` (`lhs == false`) if are trying to coerce the left-argument (right-argument).
|
| 343 |
+
This function assumes `f` is a heterogeneous operator (e.g., `HAdd.hAdd`, `HMul.hMul`, etc).
|
| 344 |
+
It returns true IF
|
| 345 |
+
- `f` is a constant of the form `Cls.op` where `Cls` is a class name, and
|
| 346 |
+
- `maxType` is of the form `C ...` where `C` is a constant, and
|
| 347 |
+
- There are more than one default instance. That is, it assumes the class `Cls` for the heterogeneous operator `f`, and
|
| 348 |
+
always has the monomorphic instance. (e.g., for `HAdd`, we have `instance [Add Ξ±] : HAdd Ξ± Ξ± Ξ±`), and
|
| 349 |
+
- If `lhs == true`, then there is a default instance of the form `Cls _ (C ..) _`, and
|
| 350 |
+
- If `lhs == false`, then there is a default instance of the form `Cls (C ..) _ _`.
|
| 351 |
+
|
| 352 |
+
The motivation is to support default instances such as
|
| 353 |
+
```
|
| 354 |
+
@[default_instance high]
|
| 355 |
+
instance [Mul Ξ±] : HMul Ξ± (Array Ξ±) (Array Ξ±) where
|
| 356 |
+
hMul a as := as.map (a * Β·)
|
| 357 |
+
|
| 358 |
+
#eval 2 * #[3, 4, 5]
|
| 359 |
+
```
|
| 360 |
+
If the type of an argument is unknown we should not coerce it to `maxType` because it would prevent
|
| 361 |
+
the default instance above from being even tried.
|
| 362 |
+
-/
|
| 363 |
+
private def hasHeterogeneousDefaultInstances (f : Expr) (maxType : Expr) (lhs : Bool) : MetaM Bool := do
|
| 364 |
+
let .const fName .. := f | return false
|
| 365 |
+
let .const typeName .. := maxType.getAppFn | return false
|
| 366 |
+
let className := fName.getPrefix
|
| 367 |
+
let defInstances β getDefaultInstances className
|
| 368 |
+
if defInstances.length β€ 1 then return false
|
| 369 |
+
for (instName, _) in defInstances do
|
| 370 |
+
if let .app (.app (.app _heteroClass lhsType) rhsType) _resultType :=
|
| 371 |
+
(β getConstInfo instName).type.getForallBody then
|
| 372 |
+
if lhs && rhsType.isAppOf typeName then return true
|
| 373 |
+
if !lhs && lhsType.isAppOf typeName then return true
|
| 374 |
+
return false
|
| 375 |
+
|
| 376 |
+
/--
|
| 377 |
+
Return `true` if polymorphic function `f` has a homogeneous instance of `maxType`.
|
| 378 |
+
The coercions to `maxType` only makes sense if such instance exists.
|
| 379 |
+
|
| 380 |
+
For example, suppose `maxType` is `Int`, and `f` is `HPow.hPow`. Then,
|
| 381 |
+
adding coercions to `maxType` only make sense if we have an instance `HPow Int Int Int`.
|
| 382 |
+
-/
|
| 383 |
+
private def hasHomogeneousInstance (f : Expr) (maxType : Expr) : MetaM Bool := do
|
| 384 |
+
let .const fName .. := f | return false
|
| 385 |
+
let className := fName.getPrefix
|
| 386 |
+
try
|
| 387 |
+
let inst β mkAppM className #[maxType, maxType, maxType]
|
| 388 |
+
return (β trySynthInstance inst) matches .some _
|
| 389 |
+
catch _ =>
|
| 390 |
+
return false
|
| 391 |
+
|
| 392 |
+
mutual
|
| 393 |
+
/--
|
| 394 |
+
Try to coerce elements in the `t` to `maxType` when needed.
|
| 395 |
+
If the type of an element in `t` is unknown we only coerce it to `maxType` if `maxType` does not have heterogeneous
|
| 396 |
+
default instances. This extra check is approximated by `hasHeterogeneousDefaultInstances`.
|
| 397 |
+
|
| 398 |
+
Remark: If `maxType` does not implement heterogeneous default instances, we do want to assign unknown types `?m` to
|
| 399 |
+
`maxType` because it produces better type information propagation. Our test suite has many tests that would break if
|
| 400 |
+
we don't do this. For example, consider the term
|
| 401 |
+
```
|
| 402 |
+
eq_of_isEqvAux a b hsz (i+1) (Nat.succ_le_of_lt h) heqv.2
|
| 403 |
+
```
|
| 404 |
+
`Nat.succ_le_of_lt h` type depends on `i+1`, but `i+1` only reduces to `Nat.succ i` if we know that `1` is a `Nat`.
|
| 405 |
+
There are several other examples like that in our test suite, and one can find them by just replacing the
|
| 406 |
+
`β hasHeterogeneousDefaultInstances f maxType lhs` test with `true`
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
Remark: if `hasHeterogeneousDefaultInstances` implementation is not good enough we should refine it in the future.
|
| 410 |
+
-/
|
| 411 |
+
private partial def applyCoe (t : Tree) (maxType : Expr) (isPred : Bool) : TermElabM Tree := do
|
| 412 |
+
go t none false isPred
|
| 413 |
+
where
|
| 414 |
+
go (t : Tree) (f? : Option Expr) (lhs : Bool) (isPred : Bool) : TermElabM Tree := do
|
| 415 |
+
match t with
|
| 416 |
+
| .binop ref .leftact f lhs rhs =>
|
| 417 |
+
return .binop ref .leftact f lhs (β go rhs none false false)
|
| 418 |
+
| .binop ref .rightact f lhs rhs =>
|
| 419 |
+
return .binop ref .rightact f (β go lhs none false false) rhs
|
| 420 |
+
| .binop ref kind f lhs rhs =>
|
| 421 |
+
/-
|
| 422 |
+
We only keep applying coercions to `maxType` if `f` is predicate or
|
| 423 |
+
`f` has a homogeneous instance with `maxType`. See `hasHomogeneousInstance` for additional details.
|
| 424 |
+
|
| 425 |
+
Remark: We assume `binrel%` elaborator is only used with homogeneous predicates.
|
| 426 |
+
-/
|
| 427 |
+
if (β pure isPred <||> hasHomogeneousInstance f maxType) then
|
| 428 |
+
return .binop ref kind f (β go lhs f true false) (β go rhs f false false)
|
| 429 |
+
else
|
| 430 |
+
let r β withRef ref do
|
| 431 |
+
mkBinOp (kind == .lazy) f (β toExpr lhs none) (β toExpr rhs none)
|
| 432 |
+
let infoTrees β getResetInfoTrees
|
| 433 |
+
return .term ref infoTrees r
|
| 434 |
+
| .unop ref f arg =>
|
| 435 |
+
return .unop ref f (β go arg none false false)
|
| 436 |
+
| .term ref trees e =>
|
| 437 |
+
let type := (β instantiateMVars (β inferType e)).cleanupAnnotations
|
| 438 |
+
trace[Elab.binop] "visiting {e} : {type} =?= {maxType}"
|
| 439 |
+
if isUnknown type then
|
| 440 |
+
if let some f := f? then
|
| 441 |
+
if (β hasHeterogeneousDefaultInstances f maxType lhs) then
|
| 442 |
+
-- See comment at `hasHeterogeneousDefaultInstances`
|
| 443 |
+
return t
|
| 444 |
+
if (β isDefEqGuarded maxType type) then
|
| 445 |
+
return t
|
| 446 |
+
else
|
| 447 |
+
trace[Elab.binop] "added coercion: {e} : {type} => {maxType}"
|
| 448 |
+
withRef ref <| return .term ref trees (β mkCoe maxType e)
|
| 449 |
+
| .macroExpansion macroName stx stx' nested =>
|
| 450 |
+
withRef stx <| withPushMacroExpansionStack stx stx' do
|
| 451 |
+
return .macroExpansion macroName stx stx' (β go nested f? lhs isPred)
|
| 452 |
+
|
| 453 |
+
private partial def toExpr (tree : Tree) (expectedType? : Option Expr) : TermElabM Expr := do
|
| 454 |
+
let r β analyze tree expectedType?
|
| 455 |
+
trace[Elab.binop] "hasUncomparable: {r.hasUncomparable}, hasUnknown: {r.hasUnknown}, maxType: {r.max?}"
|
| 456 |
+
if r.hasUncomparable || r.max?.isNone then
|
| 457 |
+
let result β toExprCore tree
|
| 458 |
+
ensureHasType expectedType? result
|
| 459 |
+
else
|
| 460 |
+
let result β toExprCore (β applyCoe tree r.max?.get! (isPred := false))
|
| 461 |
+
unless r.hasUnknown do
|
| 462 |
+
-- Record the resulting maxType calculation.
|
| 463 |
+
-- We can do this when all the types are known, since in this case `hasUncomparable` is valid.
|
| 464 |
+
-- If they're not known, recording maxType like this can lead to heterogeneous operations failing to elaborate.
|
| 465 |
+
discard <| isDefEqGuarded (β inferType result) r.max?.get!
|
| 466 |
+
trace[Elab.binop] "result: {result}"
|
| 467 |
+
ensureHasType expectedType? result
|
| 468 |
+
|
| 469 |
+
end
|
| 470 |
+
|
| 471 |
+
def elabOp : TermElab := fun stx expectedType? => do
|
| 472 |
+
toExpr (β toTree stx) expectedType?
|
| 473 |
+
|
| 474 |
+
@[builtin_term_elab binop] def elabBinOp : TermElab := elabOp
|
| 475 |
+
@[builtin_term_elab binop_lazy] def elabBinOpLazy : TermElab := elabOp
|
| 476 |
+
@[builtin_term_elab leftact] def elabLeftact : TermElab := elabOp
|
| 477 |
+
@[builtin_term_elab rightact] def elabRightact : TermElab := elabOp
|
| 478 |
+
@[builtin_term_elab unop] def elabUnOp : TermElab := elabOp
|
| 479 |
+
|
| 480 |
+
/--
|
| 481 |
+
Elaboration functions for `binrel%` and `binrel_no_prop%` notations.
|
| 482 |
+
We use the infrastructure for `binop%` to make sure we propagate information between the left and right hand sides
|
| 483 |
+
of a binary relation.
|
| 484 |
+
|
| 485 |
+
- `binrel% R x y` elaborates `R x y` using the `binop%/...` expression trees in both `x` and `y`.
|
| 486 |
+
It is similar to how `binop% R x y` elaborates but with a significant difference:
|
| 487 |
+
it does not use the expected type when computing the types of the operands.
|
| 488 |
+
- `binrel_no_prop% R x y` elaborates `R x y` like `binrel% R x y`, but if the resulting type for `x` and `y`
|
| 489 |
+
is `Prop` they are coerced to `Bool`.
|
| 490 |
+
This is used for relations such as `==` which do not support `Prop`, but we still want
|
| 491 |
+
to be able to write `(5 > 2) == (2 > 1)` for example.
|
| 492 |
+
-/
|
| 493 |
+
def elabBinRelCore (noProp : Bool) (stx : Syntax) (expectedType? : Option Expr) : TermElabM Expr := do
|
| 494 |
+
match (β resolveId? stx[1]) with
|
| 495 |
+
| some f => withSynthesizeLight do
|
| 496 |
+
/-
|
| 497 |
+
We used to use `withSynthesize (mayPostpone := true)` here instead of `withSynthesizeLight` here.
|
| 498 |
+
It seems too much to apply default instances at binary relations. For example, we cannot elaborate
|
| 499 |
+
```
|
| 500 |
+
def as : List Int := [-1, 2, 0, -3, 4]
|
| 501 |
+
#eval as.map fun a => ite (a β₯ 0) [a] []
|
| 502 |
+
```
|
| 503 |
+
The problem is that when elaborating `a β₯ 0` we don't know yet that `a` is an `Int`.
|
| 504 |
+
Then, by applying default instances, we apply the default instance to `0` that forces it to become an `Int`,
|
| 505 |
+
and Lean infers that `a` has type `Nat`.
|
| 506 |
+
Then, later we get a type error because `as` is `List Int` instead of `List Nat`.
|
| 507 |
+
This behavior is quite counterintuitive since if we avoid this elaborator by writing
|
| 508 |
+
```
|
| 509 |
+
def as : List Int := [-1, 2, 0, -3, 4]
|
| 510 |
+
#eval as.map fun a => ite (GE.ge a 0) [a] []
|
| 511 |
+
```
|
| 512 |
+
everything works.
|
| 513 |
+
However, there is a drawback of using `withSynthesizeLight` instead of `withSynthesize (mayPostpone := true)`.
|
| 514 |
+
The following cannot be elaborated
|
| 515 |
+
```
|
| 516 |
+
have : (0 == 1) = false := rfl
|
| 517 |
+
```
|
| 518 |
+
We get a type error at `rfl`. `0 == 1` only reduces to `false` after we have applied the default instances that force
|
| 519 |
+
the numeral to be `Nat`. We claim this is defensible behavior because the same happens if we do not use this elaborator.
|
| 520 |
+
```
|
| 521 |
+
have : (BEq.beq 0 1) = false := rfl
|
| 522 |
+
```
|
| 523 |
+
We can improve this failure in the future by applying default instances before reporting a type mismatch.
|
| 524 |
+
-/
|
| 525 |
+
let lhsStx := stx[2]
|
| 526 |
+
let rhsStx := stx[3]
|
| 527 |
+
let lhs β withRef lhsStx <| toTree lhsStx
|
| 528 |
+
let rhs β withRef rhsStx <| toTree rhsStx
|
| 529 |
+
let tree := .binop stx .regular f lhs rhs
|
| 530 |
+
let r β analyze tree none
|
| 531 |
+
trace[Elab.binrel] "hasUncomparable: {r.hasUncomparable}, hasUnknown: {r.hasUnknown}, maxType: {r.max?}"
|
| 532 |
+
if r.hasUncomparable || r.max?.isNone then
|
| 533 |
+
-- Use default elaboration strategy + `toBoolIfNecessary`
|
| 534 |
+
let lhs β toExprCore lhs
|
| 535 |
+
let rhs β toExprCore rhs
|
| 536 |
+
let lhs β withRef lhsStx <| toBoolIfNecessary lhs
|
| 537 |
+
let rhs β withRef rhsStx <| toBoolIfNecessary rhs
|
| 538 |
+
let lhsType β inferType lhs
|
| 539 |
+
let rhs β withRef rhsStx <| ensureHasType lhsType rhs
|
| 540 |
+
elabAppArgs f #[] #[Arg.expr lhs, Arg.expr rhs] expectedType? (explicit := false) (ellipsis := false) (resultIsOutParamSupport := false)
|
| 541 |
+
else
|
| 542 |
+
let mut maxType := r.max?.get!
|
| 543 |
+
/- If `noProp == true` and `maxType` is `Prop`, then set `maxType := Bool`. `See toBoolIfNecessary` -/
|
| 544 |
+
if noProp then
|
| 545 |
+
if (β withNewMCtxDepth <| isDefEq maxType (mkSort levelZero)) then
|
| 546 |
+
maxType := Lean.mkConst ``Bool
|
| 547 |
+
let result β toExprCore (β applyCoe tree maxType (isPred := true))
|
| 548 |
+
trace[Elab.binrel] "result: {result}"
|
| 549 |
+
return result
|
| 550 |
+
| none => throwUnknownConstantAt stx[1] stx[1].getId
|
| 551 |
+
where
|
| 552 |
+
/-- If `noProp == true` and `e` has type `Prop`, then coerce it to `Bool`. -/
|
| 553 |
+
toBoolIfNecessary (e : Expr) : TermElabM Expr := do
|
| 554 |
+
if noProp then
|
| 555 |
+
-- We use `withNewMCtxDepth` to make sure metavariables are not assigned
|
| 556 |
+
if (β withNewMCtxDepth <| isDefEq (β inferType e) (mkSort levelZero)) then
|
| 557 |
+
return (β ensureHasType (Lean.mkConst ``Bool) e)
|
| 558 |
+
return e
|
| 559 |
+
|
| 560 |
+
@[builtin_term_elab binrel] def elabBinRel : TermElab := elabBinRelCore false
|
| 561 |
+
|
| 562 |
+
@[builtin_term_elab binrel_no_prop] def elabBinRelNoProp : TermElab := elabBinRelCore true
|
| 563 |
+
|
| 564 |
+
@[builtin_term_elab defaultOrOfNonempty]
|
| 565 |
+
def elabDefaultOrNonempty : TermElab := fun stx expectedType? => do
|
| 566 |
+
tryPostponeIfNoneOrMVar expectedType?
|
| 567 |
+
match expectedType? with
|
| 568 |
+
| none => throwError "invalid 'default_or_ofNonempty%', expected type is not known"
|
| 569 |
+
| some expectedType =>
|
| 570 |
+
try
|
| 571 |
+
mkDefault expectedType
|
| 572 |
+
catch ex => try
|
| 573 |
+
mkOfNonempty expectedType
|
| 574 |
+
catch _ =>
|
| 575 |
+
if stx[1].isNone then
|
| 576 |
+
throw ex
|
| 577 |
+
else
|
| 578 |
+
-- It is in the context of an `unsafe` constant. We can use sorry instead.
|
| 579 |
+
-- Another option is to make a recursive application since it is unsafe.
|
| 580 |
+
mkLabeledSorry expectedType false (unique := true)
|
| 581 |
+
|
| 582 |
+
builtin_initialize
|
| 583 |
+
registerTraceClass `Elab.binop
|
| 584 |
+
registerTraceClass `Elab.binrel
|
| 585 |
+
|
| 586 |
+
end Op
|
| 587 |
+
|
| 588 |
+
end Lean.Elab.Term
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Frontend.lean
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Language.Lean
|
| 8 |
+
import Lean.Util.Profile
|
| 9 |
+
import Lean.Server.References
|
| 10 |
+
import Lean.Util.Profiler
|
| 11 |
+
|
| 12 |
+
namespace Lean.Elab.Frontend
|
| 13 |
+
|
| 14 |
+
structure State where
|
| 15 |
+
commandState : Command.State
|
| 16 |
+
parserState : Parser.ModuleParserState
|
| 17 |
+
cmdPos : String.Pos
|
| 18 |
+
commands : Array Syntax := #[]
|
| 19 |
+
deriving Nonempty
|
| 20 |
+
|
| 21 |
+
structure Context where
|
| 22 |
+
inputCtx : Parser.InputContext
|
| 23 |
+
|
| 24 |
+
abbrev FrontendM := ReaderT Context $ StateRefT State IO
|
| 25 |
+
|
| 26 |
+
def setCommandState (commandState : Command.State) : FrontendM Unit :=
|
| 27 |
+
modify fun s => { s with commandState := commandState }
|
| 28 |
+
|
| 29 |
+
@[inline] def runCommandElabM (x : Command.CommandElabM Ξ±) : FrontendM Ξ± := do
|
| 30 |
+
let ctx β read
|
| 31 |
+
let s β get
|
| 32 |
+
let cmdCtx : Command.Context := {
|
| 33 |
+
cmdPos := s.cmdPos
|
| 34 |
+
fileName := ctx.inputCtx.fileName
|
| 35 |
+
fileMap := ctx.inputCtx.fileMap
|
| 36 |
+
snap? := none
|
| 37 |
+
cancelTk? := none
|
| 38 |
+
}
|
| 39 |
+
match (β liftM <| EIO.toIO' <| (x cmdCtx).run s.commandState) with
|
| 40 |
+
| Except.error e => throw <| IO.Error.userError s!"unexpected internal error: {β e.toMessageData.toString}"
|
| 41 |
+
| Except.ok (a, sNew) => setCommandState sNew; return a
|
| 42 |
+
|
| 43 |
+
def elabCommandAtFrontend (stx : Syntax) : FrontendM Unit := do
|
| 44 |
+
runCommandElabM do
|
| 45 |
+
let initMsgs β modifyGet fun st => (st.messages, { st with messages := {} })
|
| 46 |
+
Command.elabCommandTopLevel stx
|
| 47 |
+
let mut msgs := (β get).messages
|
| 48 |
+
modify ({ Β· with messages := initMsgs ++ msgs })
|
| 49 |
+
|
| 50 |
+
def updateCmdPos : FrontendM Unit := do
|
| 51 |
+
modify fun s => { s with cmdPos := s.parserState.pos }
|
| 52 |
+
|
| 53 |
+
def getParserState : FrontendM Parser.ModuleParserState := do pure (β get).parserState
|
| 54 |
+
def getCommandState : FrontendM Command.State := do pure (β get).commandState
|
| 55 |
+
def setParserState (ps : Parser.ModuleParserState) : FrontendM Unit := modify fun s => { s with parserState := ps }
|
| 56 |
+
def setMessages (msgs : MessageLog) : FrontendM Unit := modify fun s => { s with commandState := { s.commandState with messages := msgs } }
|
| 57 |
+
def getInputContext : FrontendM Parser.InputContext := do pure (β read).inputCtx
|
| 58 |
+
|
| 59 |
+
def processCommand : FrontendM Bool := do
|
| 60 |
+
updateCmdPos
|
| 61 |
+
let cmdState β getCommandState
|
| 62 |
+
let ictx β getInputContext
|
| 63 |
+
let pstate β getParserState
|
| 64 |
+
let scope := cmdState.scopes.head!
|
| 65 |
+
let pmctx := { env := cmdState.env, options := scope.opts, currNamespace := scope.currNamespace, openDecls := scope.openDecls }
|
| 66 |
+
match profileit "parsing" scope.opts fun _ => Parser.parseCommand ictx pmctx pstate cmdState.messages with
|
| 67 |
+
| (cmd, ps, messages) =>
|
| 68 |
+
modify fun s => { s with commands := s.commands.push cmd }
|
| 69 |
+
setParserState ps
|
| 70 |
+
setMessages messages
|
| 71 |
+
elabCommandAtFrontend cmd
|
| 72 |
+
pure (Parser.isTerminalCommand cmd)
|
| 73 |
+
|
| 74 |
+
partial def processCommands : FrontendM Unit := do
|
| 75 |
+
let done β processCommand
|
| 76 |
+
unless done do
|
| 77 |
+
processCommands
|
| 78 |
+
|
| 79 |
+
end Frontend
|
| 80 |
+
|
| 81 |
+
open Frontend
|
| 82 |
+
|
| 83 |
+
structure IncrementalState extends State where
|
| 84 |
+
inputCtx : Parser.InputContext
|
| 85 |
+
initialSnap : Language.Lean.CommandParsedSnapshot
|
| 86 |
+
deriving Nonempty
|
| 87 |
+
|
| 88 |
+
open Language in
|
| 89 |
+
/--
|
| 90 |
+
Variant of `IO.processCommands` that allows for potential incremental reuse. Pass in the result of a
|
| 91 |
+
previous invocation done with the same state (but usually different input context) to allow for
|
| 92 |
+
reuse.
|
| 93 |
+
-/
|
| 94 |
+
partial def IO.processCommandsIncrementally (inputCtx : Parser.InputContext)
|
| 95 |
+
(parserState : Parser.ModuleParserState) (commandState : Command.State)
|
| 96 |
+
(old? : Option IncrementalState) :
|
| 97 |
+
BaseIO IncrementalState := do
|
| 98 |
+
let task β Language.Lean.processCommands inputCtx parserState commandState
|
| 99 |
+
(old?.map fun old => (old.inputCtx, old.initialSnap))
|
| 100 |
+
go task.get task #[]
|
| 101 |
+
where
|
| 102 |
+
go initialSnap t commands :=
|
| 103 |
+
let snap := t.get
|
| 104 |
+
let commands := commands.push snap
|
| 105 |
+
if let some next := snap.nextCmdSnap? then
|
| 106 |
+
go initialSnap next.task commands
|
| 107 |
+
else
|
| 108 |
+
-- Opting into reuse also enables incremental reporting, so make sure to collect messages from
|
| 109 |
+
-- all snapshots
|
| 110 |
+
let messages := toSnapshotTree initialSnap
|
| 111 |
+
|>.getAll.map (Β·.diagnostics.msgLog)
|
| 112 |
+
|>.foldl (Β· ++ Β·) {}
|
| 113 |
+
-- In contrast to messages, we should collect info trees only from the top-level command
|
| 114 |
+
-- snapshots as they subsume any info trees reported incrementally by their children.
|
| 115 |
+
let trees := commands.map (Β·.elabSnap.infoTreeSnap.get.infoTree?) |>.filterMap id |>.toPArray'
|
| 116 |
+
return {
|
| 117 |
+
commandState := { snap.elabSnap.resultSnap.get.cmdState with messages, infoState.trees := trees }
|
| 118 |
+
parserState := snap.parserState
|
| 119 |
+
cmdPos := snap.parserState.pos
|
| 120 |
+
commands := commands.map (Β·.stx)
|
| 121 |
+
inputCtx, initialSnap
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
def IO.processCommands (inputCtx : Parser.InputContext) (parserState : Parser.ModuleParserState)
|
| 125 |
+
(commandState : Command.State) : IO State := do
|
| 126 |
+
let st β IO.processCommandsIncrementally inputCtx parserState commandState none
|
| 127 |
+
return st.toState
|
| 128 |
+
|
| 129 |
+
def process (input : String) (env : Environment) (opts : Options) (fileName : Option String := none) : IO (Environment Γ MessageLog) := do
|
| 130 |
+
let fileName := fileName.getD "<input>"
|
| 131 |
+
let inputCtx := Parser.mkInputContext input fileName
|
| 132 |
+
let s β IO.processCommands inputCtx { : Parser.ModuleParserState } (Command.mkState env {} opts)
|
| 133 |
+
pure (s.commandState.env, s.commandState.messages)
|
| 134 |
+
|
| 135 |
+
def runFrontend
|
| 136 |
+
(input : String)
|
| 137 |
+
(opts : Options)
|
| 138 |
+
(fileName : String)
|
| 139 |
+
(mainModuleName : Name)
|
| 140 |
+
(trustLevel : UInt32 := 0)
|
| 141 |
+
(oleanFileName? : Option System.FilePath := none)
|
| 142 |
+
(ileanFileName? : Option System.FilePath := none)
|
| 143 |
+
(jsonOutput : Bool := false)
|
| 144 |
+
(errorOnKinds : Array Name := #[])
|
| 145 |
+
(plugins : Array System.FilePath := #[])
|
| 146 |
+
(printStats : Bool := false)
|
| 147 |
+
(setup? : Option ModuleSetup := none)
|
| 148 |
+
: IO (Option Environment) := do
|
| 149 |
+
let startTime := (β IO.monoNanosNow).toFloat / 1000000000
|
| 150 |
+
let inputCtx := Parser.mkInputContext input fileName
|
| 151 |
+
let opts := Lean.internal.cmdlineSnapshots.setIfNotSet opts true
|
| 152 |
+
-- default to async elaboration; see also `Elab.async` docs
|
| 153 |
+
let opts := Elab.async.setIfNotSet opts true
|
| 154 |
+
let ctx := { inputCtx with }
|
| 155 |
+
let setup stx := do
|
| 156 |
+
if let some setup := setup? then
|
| 157 |
+
liftM <| setup.dynlibs.forM Lean.loadDynlib
|
| 158 |
+
return .ok {
|
| 159 |
+
trustLevel
|
| 160 |
+
mainModuleName := setup.name
|
| 161 |
+
isModule := strictOr setup.isModule stx.isModule
|
| 162 |
+
imports := setup.imports?.getD stx.imports
|
| 163 |
+
plugins := plugins ++ setup.plugins
|
| 164 |
+
importArts := setup.importArts
|
| 165 |
+
-- override cmdline options with setup options
|
| 166 |
+
opts := opts.mergeBy (fun _ _ hOpt => hOpt) setup.options.toOptions
|
| 167 |
+
}
|
| 168 |
+
else
|
| 169 |
+
return .ok {
|
| 170 |
+
imports := stx.imports
|
| 171 |
+
isModule := stx.isModule
|
| 172 |
+
mainModuleName, opts, trustLevel, plugins
|
| 173 |
+
}
|
| 174 |
+
let processor := Language.Lean.process
|
| 175 |
+
let snap β processor setup none ctx
|
| 176 |
+
let snaps := Language.toSnapshotTree snap
|
| 177 |
+
let severityOverrides := errorOnKinds.foldl (Β·.insert Β· .error) {}
|
| 178 |
+
|
| 179 |
+
-- reporting should be done before any early exit from the function
|
| 180 |
+
let hasErrors β snaps.runAndReport opts jsonOutput severityOverrides
|
| 181 |
+
|
| 182 |
+
let some cmdState := Language.Lean.waitForFinalCmdState? snap
|
| 183 |
+
| return none
|
| 184 |
+
let env := cmdState.env
|
| 185 |
+
let finalOpts := cmdState.scopes[0]!.opts
|
| 186 |
+
|
| 187 |
+
-- stats should be displayed even if there are (non-import) errors
|
| 188 |
+
if printStats then
|
| 189 |
+
env.displayStats
|
| 190 |
+
|
| 191 |
+
if hasErrors then
|
| 192 |
+
return none
|
| 193 |
+
|
| 194 |
+
if let some oleanFileName := oleanFileName? then
|
| 195 |
+
profileitIO ".olean serialization" finalOpts do
|
| 196 |
+
writeModule env oleanFileName
|
| 197 |
+
|
| 198 |
+
if let some ileanFileName := ileanFileName? then
|
| 199 |
+
let trees := snaps.getAll.flatMap (match Β·.infoTree? with | some t => #[t] | _ => #[])
|
| 200 |
+
let references := Lean.Server.findModuleRefs inputCtx.fileMap trees (localVars := false)
|
| 201 |
+
let ilean := {
|
| 202 |
+
module := mainModuleName
|
| 203 |
+
directImports := Server.collectImports β¨snap.stxβ©
|
| 204 |
+
references := β references.toLspModuleRefs
|
| 205 |
+
: Lean.Server.Ilean
|
| 206 |
+
}
|
| 207 |
+
IO.FS.writeFile ileanFileName $ Json.compress $ toJson ilean
|
| 208 |
+
|
| 209 |
+
if let some out := trace.profiler.output.get? opts then
|
| 210 |
+
let traceStates := snaps.getAll.map (Β·.traces)
|
| 211 |
+
let profile β Firefox.Profile.export mainModuleName.toString startTime traceStates opts
|
| 212 |
+
IO.FS.writeFile β¨outβ© <| Json.compress <| toJson profile
|
| 213 |
+
|
| 214 |
+
-- no point in freeing the snapshot graph and all referenced data this close to process exit
|
| 215 |
+
Runtime.forget snaps
|
| 216 |
+
return some env
|
| 217 |
+
|
| 218 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/GenInjective.lean
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Command
|
| 8 |
+
import Lean.Meta.Injective
|
| 9 |
+
|
| 10 |
+
namespace Lean.Elab.Command
|
| 11 |
+
|
| 12 |
+
@[builtin_command_elab genInjectiveTheorems] def elabGenInjectiveTheorems : CommandElab := fun stx => do
|
| 13 |
+
liftTermElabM do
|
| 14 |
+
let declName β realizeGlobalConstNoOverloadWithInfo stx[1]
|
| 15 |
+
Meta.mkInjectiveTheorems declName
|
| 16 |
+
|
| 17 |
+
end Lean.Elab.Command
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/GuardMsgs.lean
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2023 Kyle Miller. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Kyle Miller
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.Notation
|
| 8 |
+
import Lean.Util.Diff
|
| 9 |
+
import Lean.Server.CodeActions.Attr
|
| 10 |
+
|
| 11 |
+
/-! `#guard_msgs` command for testing commands
|
| 12 |
+
|
| 13 |
+
This module defines a command to test that another command produces the expected messages.
|
| 14 |
+
See the docstring on the `#guard_msgs` command.
|
| 15 |
+
-/
|
| 16 |
+
|
| 17 |
+
open Lean Parser.Tactic Elab Command
|
| 18 |
+
|
| 19 |
+
register_builtin_option guard_msgs.diff : Bool := {
|
| 20 |
+
defValue := true
|
| 21 |
+
descr := "When true, show a diff between expected and actual messages if they don't match. "
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
namespace Lean.Elab.Tactic.GuardMsgs
|
| 26 |
+
|
| 27 |
+
/-- Gives a string representation of a message without source position information.
|
| 28 |
+
Ensures the message ends with a '\n'. -/
|
| 29 |
+
private def messageToStringWithoutPos (msg : Message) : BaseIO String := do
|
| 30 |
+
let mut str β msg.data.toString
|
| 31 |
+
unless msg.caption == "" do
|
| 32 |
+
str := msg.caption ++ ":\n" ++ str
|
| 33 |
+
if !("\n".isPrefixOf str) then str := " " ++ str
|
| 34 |
+
if msg.isTrace then
|
| 35 |
+
str := "trace:" ++ str
|
| 36 |
+
else
|
| 37 |
+
match msg.severity with
|
| 38 |
+
| MessageSeverity.information => str := "info:" ++ str
|
| 39 |
+
| MessageSeverity.warning => str := "warning:" ++ str
|
| 40 |
+
| MessageSeverity.error => str := "error:" ++ str
|
| 41 |
+
if str.isEmpty || str.back != '\n' then
|
| 42 |
+
str := str ++ "\n"
|
| 43 |
+
return str
|
| 44 |
+
|
| 45 |
+
/-- The decision made by a specification for a message. -/
|
| 46 |
+
inductive SpecResult
|
| 47 |
+
/-- Capture the message and check it matches the docstring. -/
|
| 48 |
+
| check
|
| 49 |
+
/-- Drop the message and delete it. -/
|
| 50 |
+
| drop
|
| 51 |
+
/-- Do not capture the message. -/
|
| 52 |
+
| pass
|
| 53 |
+
|
| 54 |
+
/-- The method to use when normalizing whitespace, after trimming. -/
|
| 55 |
+
inductive WhitespaceMode
|
| 56 |
+
/-- Exact equality. -/
|
| 57 |
+
| exact
|
| 58 |
+
/-- Equality after normalizing newlines into spaces. -/
|
| 59 |
+
| normalized
|
| 60 |
+
/-- Equality after collapsing whitespace into single spaces. -/
|
| 61 |
+
| lax
|
| 62 |
+
|
| 63 |
+
/-- Method to use when combining multiple messages. -/
|
| 64 |
+
inductive MessageOrdering
|
| 65 |
+
/-- Use the exact ordering of the produced messages. -/
|
| 66 |
+
| exact
|
| 67 |
+
/-- Sort the produced messages. -/
|
| 68 |
+
| sorted
|
| 69 |
+
|
| 70 |
+
def parseGuardMsgsFilterAction (action? : Option (TSyntax ``guardMsgsFilterAction)) :
|
| 71 |
+
CommandElabM SpecResult := do
|
| 72 |
+
if let some action := action? then
|
| 73 |
+
match action with
|
| 74 |
+
| `(guardMsgsFilterAction| check) => pure .check
|
| 75 |
+
| `(guardMsgsFilterAction| drop) => pure .drop
|
| 76 |
+
| `(guardMsgsFilterAction| pass) => pure .pass
|
| 77 |
+
| _ => throwUnsupportedSyntax
|
| 78 |
+
else
|
| 79 |
+
pure .check
|
| 80 |
+
|
| 81 |
+
def parseGuardMsgsFilterSeverity : TSyntax ``guardMsgsFilterSeverity β CommandElabM (Message β Bool)
|
| 82 |
+
| `(guardMsgsFilterSeverity| trace) => pure fun msg => msg.isTrace
|
| 83 |
+
| `(guardMsgsFilterSeverity| info) => pure fun msg => !msg.isTrace && msg.severity == .information
|
| 84 |
+
| `(guardMsgsFilterSeverity| warning) => pure fun msg => !msg.isTrace && msg.severity == .warning
|
| 85 |
+
| `(guardMsgsFilterSeverity| error) => pure fun msg => !msg.isTrace && msg.severity == .error
|
| 86 |
+
| `(guardMsgsFilterSeverity| all) => pure fun _ => true
|
| 87 |
+
| _ => throwUnsupportedSyntax
|
| 88 |
+
|
| 89 |
+
/-- Parses a `guardMsgsSpec`.
|
| 90 |
+
- No specification: check everything.
|
| 91 |
+
- With a specification: interpret the spec, and if nothing applies pass it through. -/
|
| 92 |
+
def parseGuardMsgsSpec (spec? : Option (TSyntax ``guardMsgsSpec)) :
|
| 93 |
+
CommandElabM (WhitespaceMode Γ MessageOrdering Γ (Message β SpecResult)) := do
|
| 94 |
+
let elts β
|
| 95 |
+
if let some spec := spec? then
|
| 96 |
+
match spec with
|
| 97 |
+
| `(guardMsgsSpec| ($[$elts:guardMsgsSpecElt],*)) => pure elts
|
| 98 |
+
| _ => throwUnsupportedSyntax
|
| 99 |
+
else
|
| 100 |
+
pure #[]
|
| 101 |
+
let mut whitespace : WhitespaceMode := .normalized
|
| 102 |
+
let mut ordering : MessageOrdering := .exact
|
| 103 |
+
let mut p? : Option (Message β SpecResult) := none
|
| 104 |
+
let pushP (action : SpecResult) (msgP : Message β Bool) (p? : Option (Message β SpecResult))
|
| 105 |
+
(msg : Message) : SpecResult :=
|
| 106 |
+
if msgP msg then
|
| 107 |
+
action
|
| 108 |
+
else
|
| 109 |
+
(p?.getD fun _ => .pass) msg
|
| 110 |
+
for elt in elts.reverse do
|
| 111 |
+
match elt with
|
| 112 |
+
| `(guardMsgsSpecElt| $[$action?]? $sev) => p? := pushP (β parseGuardMsgsFilterAction action?) (β parseGuardMsgsFilterSeverity sev) p?
|
| 113 |
+
| `(guardMsgsSpecElt| whitespace := exact) => whitespace := .exact
|
| 114 |
+
| `(guardMsgsSpecElt| whitespace := normalized) => whitespace := .normalized
|
| 115 |
+
| `(guardMsgsSpecElt| whitespace := lax) => whitespace := .lax
|
| 116 |
+
| `(guardMsgsSpecElt| ordering := exact) => ordering := .exact
|
| 117 |
+
| `(guardMsgsSpecElt| ordering := sorted) => ordering := .sorted
|
| 118 |
+
| _ => throwUnsupportedSyntax
|
| 119 |
+
let defaultP := fun _ => .check
|
| 120 |
+
return (whitespace, ordering, p?.getD defaultP)
|
| 121 |
+
|
| 122 |
+
/-- An info tree node corresponding to a failed `#guard_msgs` invocation,
|
| 123 |
+
used for code action support. -/
|
| 124 |
+
structure GuardMsgFailure where
|
| 125 |
+
/-- The result of the nested command -/
|
| 126 |
+
res : String
|
| 127 |
+
deriving TypeName
|
| 128 |
+
|
| 129 |
+
/--
|
| 130 |
+
Makes trailing whitespace visible and protectes them against trimming by the editor, by appending
|
| 131 |
+
the symbol β to such a line (and also to any line that ends with such a symbol, to avoid
|
| 132 |
+
ambiguities in the case the message already had that symbol).
|
| 133 |
+
-/
|
| 134 |
+
def revealTrailingWhitespace (s : String) : String :=
|
| 135 |
+
s.replace "β\n" "ββ\n" |>.replace "\t\n" "\tβ\n" |>.replace " \n" " β\n"
|
| 136 |
+
|
| 137 |
+
/- The inverse of `revealTrailingWhitespace` -/
|
| 138 |
+
def removeTrailingWhitespaceMarker (s : String) : String :=
|
| 139 |
+
s.replace "β\n" "\n"
|
| 140 |
+
|
| 141 |
+
/--
|
| 142 |
+
Applies a whitespace normalization mode.
|
| 143 |
+
-/
|
| 144 |
+
def WhitespaceMode.apply (mode : WhitespaceMode) (s : String) : String :=
|
| 145 |
+
match mode with
|
| 146 |
+
| .exact => s
|
| 147 |
+
| .normalized => s.replace "\n" " "
|
| 148 |
+
| .lax => String.intercalate " " <| (s.split Char.isWhitespace).filter (!Β·.isEmpty)
|
| 149 |
+
|
| 150 |
+
/--
|
| 151 |
+
Applies a message ordering mode.
|
| 152 |
+
-/
|
| 153 |
+
def MessageOrdering.apply (mode : MessageOrdering) (msgs : List String) : List String :=
|
| 154 |
+
match mode with
|
| 155 |
+
| .exact => msgs
|
| 156 |
+
| .sorted => msgs |>.toArray.qsort (Β· < Β·) |>.toList
|
| 157 |
+
|
| 158 |
+
@[builtin_command_elab Lean.guardMsgsCmd] def elabGuardMsgs : CommandElab
|
| 159 |
+
| `(command| $[$dc?:docComment]? #guard_msgs%$tk $(spec?)? in $cmd) => do
|
| 160 |
+
let expected : String := (β dc?.mapM (getDocStringText Β·)).getD ""
|
| 161 |
+
|>.trim |> removeTrailingWhitespaceMarker
|
| 162 |
+
let (whitespace, ordering, specFn) β parseGuardMsgsSpec spec?
|
| 163 |
+
let initMsgs β modifyGet fun st => (st.messages, { st with messages := {} })
|
| 164 |
+
-- do not forward snapshot as we don't want messages assigned to it to leak outside
|
| 165 |
+
withReader ({ Β· with snap? := none }) do
|
| 166 |
+
-- The `#guard_msgs` command is special-cased in `elabCommandTopLevel` to ensure linters only run once.
|
| 167 |
+
elabCommandTopLevel cmd
|
| 168 |
+
-- collect sync and async messages
|
| 169 |
+
let msgs := (β get).messages ++
|
| 170 |
+
(β get).snapshotTasks.foldl (Β· ++ Β·.get.getAll.foldl (Β· ++ Β·.diagnostics.msgLog) {}) {}
|
| 171 |
+
-- clear async messages as we don't want them to leak outside
|
| 172 |
+
modify ({ Β· with snapshotTasks := #[] })
|
| 173 |
+
let mut toCheck : MessageLog := .empty
|
| 174 |
+
let mut toPassthrough : MessageLog := .empty
|
| 175 |
+
for msg in msgs.toList do
|
| 176 |
+
if msg.isSilent then
|
| 177 |
+
continue
|
| 178 |
+
match specFn msg with
|
| 179 |
+
| .check => toCheck := toCheck.add msg
|
| 180 |
+
| .drop => pure ()
|
| 181 |
+
| pass => toPassthrough := toPassthrough.add msg
|
| 182 |
+
let strings β toCheck.toList.mapM (messageToStringWithoutPos Β·)
|
| 183 |
+
let strings := ordering.apply strings
|
| 184 |
+
let res := "---\n".intercalate strings |>.trim
|
| 185 |
+
if whitespace.apply expected == whitespace.apply res then
|
| 186 |
+
-- Passed. Only put toPassthrough messages back on the message log
|
| 187 |
+
modify fun st => { st with messages := initMsgs ++ toPassthrough }
|
| 188 |
+
else
|
| 189 |
+
-- Failed. Put all the messages back on the message log and add an error
|
| 190 |
+
modify fun st => { st with messages := initMsgs ++ msgs }
|
| 191 |
+
let feedback :=
|
| 192 |
+
if guard_msgs.diff.get (β getOptions) then
|
| 193 |
+
let diff := Diff.diff (expected.split (Β· == '\n')).toArray (res.split (Β· == '\n')).toArray
|
| 194 |
+
Diff.linesToString diff
|
| 195 |
+
else res
|
| 196 |
+
logErrorAt tk m!"βοΈ Docstring on `#guard_msgs` does not match generated message:\n\n{feedback}"
|
| 197 |
+
pushInfoLeaf (.ofCustomInfo { stx := β getRef, value := Dynamic.mk (GuardMsgFailure.mk res) })
|
| 198 |
+
| _ => throwUnsupportedSyntax
|
| 199 |
+
|
| 200 |
+
open CodeAction Server RequestM in
|
| 201 |
+
/-- A code action which will update the doc comment on a `#guard_msgs` invocation. -/
|
| 202 |
+
@[builtin_command_code_action guardMsgsCmd]
|
| 203 |
+
def guardMsgsCodeAction : CommandCodeAction := fun _ _ _ node => do
|
| 204 |
+
let .node _ ts := node | return #[]
|
| 205 |
+
let res := ts.findSome? fun
|
| 206 |
+
| .node (.ofCustomInfo { stx, value }) _ => return (stx, (β value.get? GuardMsgFailure).res)
|
| 207 |
+
| _ => none
|
| 208 |
+
let some (stx, res) := res | return #[]
|
| 209 |
+
let doc β readDoc
|
| 210 |
+
let eager := {
|
| 211 |
+
title := "Update #guard_msgs with tactic output"
|
| 212 |
+
kind? := "quickfix"
|
| 213 |
+
isPreferred? := true
|
| 214 |
+
}
|
| 215 |
+
pure #[{
|
| 216 |
+
eager
|
| 217 |
+
lazy? := some do
|
| 218 |
+
let some start := stx.getPos? true | return eager
|
| 219 |
+
let some tail := stx.setArg 0 mkNullNode |>.getPos? true | return eager
|
| 220 |
+
let res := revealTrailingWhitespace res
|
| 221 |
+
let newText := if res.isEmpty then
|
| 222 |
+
""
|
| 223 |
+
else if res.length β€ 100-7 && !res.contains '\n' then -- TODO: configurable line length?
|
| 224 |
+
s!"/-- {res} -/\n"
|
| 225 |
+
else
|
| 226 |
+
s!"/--\n{res}\n-/\n"
|
| 227 |
+
pure { eager with
|
| 228 |
+
edit? := some <|.ofTextEdit doc.versionedIdentifier {
|
| 229 |
+
range := doc.meta.text.utf8RangeToLspRange β¨start, tailβ©
|
| 230 |
+
newText
|
| 231 |
+
}
|
| 232 |
+
}
|
| 233 |
+
}]
|
| 234 |
+
|
| 235 |
+
end Lean.Elab.Tactic.GuardMsgs
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Import.lean
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Sebastian Ullrich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Parser.Module
|
| 8 |
+
import Lean.CoreM
|
| 9 |
+
|
| 10 |
+
namespace Lean.Elab
|
| 11 |
+
|
| 12 |
+
abbrev HeaderSyntax := TSyntax ``Parser.Module.header
|
| 13 |
+
|
| 14 |
+
def HeaderSyntax.startPos (header : HeaderSyntax) : String.Pos :=
|
| 15 |
+
header.raw.getPos?.getD 0
|
| 16 |
+
|
| 17 |
+
def HeaderSyntax.isModule (header : HeaderSyntax) : Bool :=
|
| 18 |
+
!header.raw[0].isNone
|
| 19 |
+
|
| 20 |
+
def HeaderSyntax.imports (stx : HeaderSyntax) (includeInit : Bool := true) : Array Import :=
|
| 21 |
+
match stx with
|
| 22 |
+
| `(Parser.Module.header| $[module%$moduleTk]? $[prelude%$preludeTk]? $importsStx*) =>
|
| 23 |
+
let imports := if preludeTk.isNone && includeInit then #[{ module := `Init : Import }] else #[]
|
| 24 |
+
imports ++ importsStx.map fun
|
| 25 |
+
| `(Parser.Module.import| $[public%$publicTk]? $[meta%$metaTk]? import $[all%$allTk]? $n) =>
|
| 26 |
+
{ module := n.getId, importAll := allTk.isSome
|
| 27 |
+
isExported := publicTk.isSome || moduleTk.isNone
|
| 28 |
+
isMeta := metaTk.isSome }
|
| 29 |
+
| _ => unreachable!
|
| 30 |
+
| _ => unreachable!
|
| 31 |
+
|
| 32 |
+
def HeaderSyntax.toModuleHeader (stx : HeaderSyntax) : ModuleHeader where
|
| 33 |
+
isModule := stx.isModule
|
| 34 |
+
imports := stx.imports
|
| 35 |
+
|
| 36 |
+
abbrev headerToImports := @HeaderSyntax.imports
|
| 37 |
+
|
| 38 |
+
def processHeaderCore
|
| 39 |
+
(startPos : String.Pos) (imports : Array Import) (isModule : Bool)
|
| 40 |
+
(opts : Options) (messages : MessageLog) (inputCtx : Parser.InputContext)
|
| 41 |
+
(trustLevel : UInt32 := 0) (plugins : Array System.FilePath := #[]) (leakEnv := false)
|
| 42 |
+
(mainModule := Name.anonymous) (arts : NameMap ImportArtifacts := {})
|
| 43 |
+
: IO (Environment Γ MessageLog) := do
|
| 44 |
+
let level := if isModule then
|
| 45 |
+
if Elab.inServer.get opts then
|
| 46 |
+
.server
|
| 47 |
+
else
|
| 48 |
+
.exported
|
| 49 |
+
else
|
| 50 |
+
.private
|
| 51 |
+
let (env, messages) β try
|
| 52 |
+
for i in imports do
|
| 53 |
+
if !isModule && i.importAll then
|
| 54 |
+
throw <| .userError "cannot use `import all` without `module`"
|
| 55 |
+
if i.importAll && mainModule.getRoot != i.module.getRoot then
|
| 56 |
+
throw <| .userError "cannot use `import all` across module path roots"
|
| 57 |
+
let env β
|
| 58 |
+
importModules (leakEnv := leakEnv) (loadExts := true) (level := level)
|
| 59 |
+
imports opts trustLevel plugins arts
|
| 60 |
+
pure (env, messages)
|
| 61 |
+
catch e =>
|
| 62 |
+
let env β mkEmptyEnvironment
|
| 63 |
+
let pos := inputCtx.fileMap.toPosition startPos
|
| 64 |
+
pure (env, messages.add { fileName := inputCtx.fileName, data := toString e, pos := pos })
|
| 65 |
+
return (env.setMainModule mainModule, messages)
|
| 66 |
+
|
| 67 |
+
/--
|
| 68 |
+
Elaborates the given header syntax into an environment.
|
| 69 |
+
|
| 70 |
+
If `mainModule` is not given, `Environment.setMainModule` should be called manually. This is a
|
| 71 |
+
backwards compatibility measure not compatible with the module system.
|
| 72 |
+
-/
|
| 73 |
+
@[inline] def processHeader
|
| 74 |
+
(header : HeaderSyntax)
|
| 75 |
+
(opts : Options) (messages : MessageLog) (inputCtx : Parser.InputContext)
|
| 76 |
+
(trustLevel : UInt32 := 0) (plugins : Array System.FilePath := #[]) (leakEnv := false)
|
| 77 |
+
(mainModule := Name.anonymous)
|
| 78 |
+
: IO (Environment Γ MessageLog) := do
|
| 79 |
+
processHeaderCore header.startPos header.imports header.isModule
|
| 80 |
+
opts messages inputCtx trustLevel plugins leakEnv mainModule
|
| 81 |
+
|
| 82 |
+
def parseImports (input : String) (fileName : Option String := none) : IO (Array Import Γ Position Γ MessageLog) := do
|
| 83 |
+
let fileName := fileName.getD "<input>"
|
| 84 |
+
let inputCtx := Parser.mkInputContext input fileName
|
| 85 |
+
let (header, parserState, messages) β Parser.parseHeader inputCtx
|
| 86 |
+
pure (headerToImports header, inputCtx.fileMap.toPosition parserState.pos, messages)
|
| 87 |
+
|
| 88 |
+
def printImports (input : String) (fileName : Option String) : IO Unit := do
|
| 89 |
+
let (deps, _, _) β parseImports input fileName
|
| 90 |
+
for dep in deps do
|
| 91 |
+
let fname β findOLean dep.module
|
| 92 |
+
IO.println fname
|
| 93 |
+
|
| 94 |
+
def printImportSrcs (input : String) (fileName : Option String) : IO Unit := do
|
| 95 |
+
let sp β getSrcSearchPath
|
| 96 |
+
let (deps, _, _) β parseImports input fileName
|
| 97 |
+
for dep in deps do
|
| 98 |
+
let fname β findLean sp dep.module
|
| 99 |
+
IO.println fname
|
| 100 |
+
|
| 101 |
+
end Lean.Elab
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/Inductive.lean
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura, Kyle Miller
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Elab.MutualInductive
|
| 8 |
+
|
| 9 |
+
namespace Lean.Elab.Command
|
| 10 |
+
open Meta
|
| 11 |
+
|
| 12 |
+
/-
|
| 13 |
+
```
|
| 14 |
+
def Lean.Parser.Command.Β«inductiveΒ» :=
|
| 15 |
+
leading_parser "inductive " >> declId >> optDeclSig >> optional ("where" <|> ":=") >> many ctor
|
| 16 |
+
|
| 17 |
+
def Lean.Parser.Command.classInductive :=
|
| 18 |
+
leading_parser atomic (group ("class " >> "inductive ")) >> declId >> optDeclSig >> optional ("where" <|> ":=") >> many ctor >> optDeriving
|
| 19 |
+
```
|
| 20 |
+
-/
|
| 21 |
+
private def inductiveSyntaxToView (modifiers : Modifiers) (decl : Syntax) : TermElabM InductiveView := do
|
| 22 |
+
let isClass := decl.isOfKind ``Parser.Command.classInductive
|
| 23 |
+
let modifiers := if isClass then modifiers.addAttr { name := `class } else modifiers
|
| 24 |
+
let (binders, type?) := expandOptDeclSig decl[2]
|
| 25 |
+
let declId := decl[1]
|
| 26 |
+
let β¨name, declName, levelNamesβ© β Term.expandDeclId (β getCurrNamespace) (β Term.getLevelNames) declId modifiers
|
| 27 |
+
addDeclarationRangesForBuiltin declName modifiers.stx decl
|
| 28 |
+
let ctors β decl[4].getArgs.mapM fun ctor => withRef ctor do
|
| 29 |
+
/-
|
| 30 |
+
```
|
| 31 |
+
def ctor := leading_parser optional docComment >> "\n| " >> declModifiers >> rawIdent >> optDeclSig
|
| 32 |
+
```
|
| 33 |
+
-/
|
| 34 |
+
let mut ctorModifiers β elabModifiers β¨ctor[2]β©
|
| 35 |
+
if let some leadingDocComment := ctor[0].getOptional? then
|
| 36 |
+
if ctorModifiers.docString?.isSome then
|
| 37 |
+
logErrorAt leadingDocComment "duplicate doc string"
|
| 38 |
+
ctorModifiers := { ctorModifiers with docString? := some β¨leadingDocCommentβ© }
|
| 39 |
+
if ctorModifiers.isPrivate && modifiers.isPrivate then
|
| 40 |
+
throwError "invalid 'private' constructor in a 'private' inductive datatype"
|
| 41 |
+
if ctorModifiers.isProtected && modifiers.isPrivate then
|
| 42 |
+
throwError "invalid 'protected' constructor in a 'private' inductive datatype"
|
| 43 |
+
checkValidCtorModifier ctorModifiers
|
| 44 |
+
let ctorName := ctor.getIdAt 3
|
| 45 |
+
let ctorName := declName ++ ctorName
|
| 46 |
+
let ctorName β withRef ctor[3] <| applyVisibility ctorModifiers.visibility ctorName
|
| 47 |
+
let (binders, type?) := expandOptDeclSig ctor[4]
|
| 48 |
+
addDocString' ctorName ctorModifiers.docString?
|
| 49 |
+
addDeclarationRangesFromSyntax ctorName ctor ctor[3]
|
| 50 |
+
return { ref := ctor, declId := ctor[3], modifiers := ctorModifiers, declName := ctorName, binders := binders, type? := type? : CtorView }
|
| 51 |
+
let computedFields β (decl[5].getOptional?.map (Β·[1].getArgs) |>.getD #[]).mapM fun cf => withRef cf do
|
| 52 |
+
return { ref := cf, modifiers := cf[0], fieldId := cf[1].getId, type := β¨cf[3]β©, matchAlts := β¨cf[4]β© }
|
| 53 |
+
let classes β getOptDerivingClasses decl[6]
|
| 54 |
+
if decl[3][0].isToken ":=" then
|
| 55 |
+
-- https://github.com/leanprover/lean4/issues/5236
|
| 56 |
+
withRef decl[0] <| Linter.logLintIf Linter.linter.deprecated decl[3]
|
| 57 |
+
"'inductive ... :=' has been deprecated in favor of 'inductive ... where'."
|
| 58 |
+
return {
|
| 59 |
+
ref := decl
|
| 60 |
+
shortDeclName := name
|
| 61 |
+
derivingClasses := classes
|
| 62 |
+
allowIndices := true
|
| 63 |
+
allowSortPolymorphism := true
|
| 64 |
+
declId, modifiers, isClass, declName, levelNames
|
| 65 |
+
binders, type?, ctors
|
| 66 |
+
computedFields
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
private def isInductiveFamily (numParams : Nat) (indFVar : Expr) : TermElabM Bool := do
|
| 70 |
+
let indFVarType β inferType indFVar
|
| 71 |
+
forallTelescopeReducing indFVarType fun xs _ =>
|
| 72 |
+
return xs.size > numParams
|
| 73 |
+
|
| 74 |
+
private def getArrowBinderNames (type : Expr) : Array Name :=
|
| 75 |
+
go type #[]
|
| 76 |
+
where
|
| 77 |
+
go (type : Expr) (acc : Array Name) : Array Name :=
|
| 78 |
+
match type with
|
| 79 |
+
| .forallE n _ b _ => go b (acc.push n)
|
| 80 |
+
| .mdata _ b => go b acc
|
| 81 |
+
| _ => acc
|
| 82 |
+
|
| 83 |
+
/--
|
| 84 |
+
Replaces binder names in `type` with `newNames`.
|
| 85 |
+
Remark: we only replace the names for binder containing macroscopes.
|
| 86 |
+
-/
|
| 87 |
+
private def replaceArrowBinderNames (type : Expr) (newNames : Array Name) : Expr :=
|
| 88 |
+
go type 0
|
| 89 |
+
where
|
| 90 |
+
go (type : Expr) (i : Nat) : Expr :=
|
| 91 |
+
if h : i < newNames.size then
|
| 92 |
+
match type with
|
| 93 |
+
| .forallE n d b bi =>
|
| 94 |
+
if n.hasMacroScopes then
|
| 95 |
+
mkForall newNames[i] bi d (go b (i+1))
|
| 96 |
+
else
|
| 97 |
+
mkForall n bi d (go b (i+1))
|
| 98 |
+
| _ => type
|
| 99 |
+
else
|
| 100 |
+
type
|
| 101 |
+
|
| 102 |
+
/--
|
| 103 |
+
Reorders constructor arguments to improve the effectiveness of the `fixedIndicesToParams` method.
|
| 104 |
+
|
| 105 |
+
The idea is quite simple. Given a constructor type of the form
|
| 106 |
+
```
|
| 107 |
+
(aβ : Aβ) β ... β (aβ : Aβ) β C bβ ... bβ
|
| 108 |
+
```
|
| 109 |
+
We try to find the longest prefix `bβ ... bα΅’`, `i β€ m` s.t.
|
| 110 |
+
- each `bβ` is in `{aβ, ..., aβ}`
|
| 111 |
+
- each `bβ` only depends on variables in `{bβ, ..., bβββ}`
|
| 112 |
+
|
| 113 |
+
Then, it moves this prefix `bβ ... bα΅’` to the front.
|
| 114 |
+
|
| 115 |
+
Remark: We only reorder implicit arguments that have macroscopes. See issue #1156.
|
| 116 |
+
The macroscope test is an approximation, we could have restricted ourselves to auto-implicit arguments.
|
| 117 |
+
-/
|
| 118 |
+
private def reorderCtorArgs (ctorType : Expr) : MetaM Expr := do
|
| 119 |
+
forallTelescopeReducing ctorType fun as type => do
|
| 120 |
+
/- `type` is of the form `C ...` where `C` is the inductive datatype being defined. -/
|
| 121 |
+
let bs := type.getAppArgs
|
| 122 |
+
let mut as := as
|
| 123 |
+
let mut bsPrefix := #[]
|
| 124 |
+
for b in bs do
|
| 125 |
+
unless b.isFVar && as.contains b do
|
| 126 |
+
break
|
| 127 |
+
let localDecl β getFVarLocalDecl b
|
| 128 |
+
if localDecl.binderInfo.isExplicit then
|
| 129 |
+
break
|
| 130 |
+
unless localDecl.userName.hasMacroScopes do
|
| 131 |
+
break
|
| 132 |
+
if (β localDeclDependsOnPred localDecl fun fvarId => as.any fun p => p.fvarId! == fvarId) then
|
| 133 |
+
break
|
| 134 |
+
bsPrefix := bsPrefix.push b
|
| 135 |
+
as := as.erase b
|
| 136 |
+
if bsPrefix.isEmpty then
|
| 137 |
+
return ctorType
|
| 138 |
+
else
|
| 139 |
+
let r β mkForallFVars (bsPrefix ++ as) type
|
| 140 |
+
/- `r` already contains the resulting type.
|
| 141 |
+
To be able to produce better error messages, we copy the first `bsPrefix.size` binder names from `C` to `r`.
|
| 142 |
+
This is important when some of constructor parameters were inferred using the auto-bound implicit feature.
|
| 143 |
+
For example, in the following declaration.
|
| 144 |
+
```
|
| 145 |
+
inductive Member : Ξ± β List Ξ± β Type u
|
| 146 |
+
| head : Member a (a::as)
|
| 147 |
+
| tail : Member a bs β Member a (b::bs)
|
| 148 |
+
```
|
| 149 |
+
if we do not copy the binder names
|
| 150 |
+
```
|
| 151 |
+
#check @Member.head
|
| 152 |
+
```
|
| 153 |
+
produces `@Member.head : {x : Type u_1} β {a : x} β {as : List x} β Member a (a :: as)`
|
| 154 |
+
which is correct, but a bit confusing. By copying the binder names, we obtain
|
| 155 |
+
`@Member.head : {Ξ± : Type u_1} β {a : Ξ±} β {as : List Ξ±} β Member a (a :: as)`
|
| 156 |
+
-/
|
| 157 |
+
let C := type.getAppFn
|
| 158 |
+
let binderNames := getArrowBinderNames (β instantiateMVars (β inferType C))
|
| 159 |
+
return replaceArrowBinderNames r binderNames[*...bsPrefix.size]
|
| 160 |
+
|
| 161 |
+
/--
|
| 162 |
+
Elaborate constructor types.
|
| 163 |
+
|
| 164 |
+
Remark: we check whether the resulting type is correct, and the parameter occurrences are consistent, but
|
| 165 |
+
we currently do not check for:
|
| 166 |
+
- Positivity (it is a rare failure, and the kernel already checks for it).
|
| 167 |
+
- Universe constraints (the kernel checks for it).
|
| 168 |
+
-/
|
| 169 |
+
private def elabCtors (indFVars : Array Expr) (params : Array Expr) (r : ElabHeaderResult) : TermElabM (List Constructor) :=
|
| 170 |
+
withRef r.view.ref do
|
| 171 |
+
withExplicitToImplicit params do
|
| 172 |
+
let indFVar := r.indFVar
|
| 173 |
+
let indFamily β isInductiveFamily params.size indFVar
|
| 174 |
+
r.view.ctors.toList.mapM fun ctorView =>
|
| 175 |
+
Term.withAutoBoundImplicit <| Term.elabBinders ctorView.binders.getArgs fun ctorParams =>
|
| 176 |
+
withRef ctorView.ref do
|
| 177 |
+
let elabCtorType : TermElabM Expr := do
|
| 178 |
+
match ctorView.type? with
|
| 179 |
+
| none =>
|
| 180 |
+
if indFamily then
|
| 181 |
+
throwError "Missing resulting type for constructor '{ctorView.declName}': \
|
| 182 |
+
Its resulting type must be specified because it is part of an inductive family declaration"
|
| 183 |
+
return mkAppN indFVar params
|
| 184 |
+
| some ctorType =>
|
| 185 |
+
let type β Term.elabType ctorType
|
| 186 |
+
trace[Elab.inductive] "elabType {ctorView.declName} : {type} "
|
| 187 |
+
Term.synthesizeSyntheticMVars (postpone := .yes)
|
| 188 |
+
let type β instantiateMVars type
|
| 189 |
+
let type β checkParamOccs type
|
| 190 |
+
forallTelescopeReducing type fun _ resultingType => do
|
| 191 |
+
unless resultingType.getAppFn == indFVar do
|
| 192 |
+
throwUnexpectedResultingTypeMismatch resultingType indFVar ctorView.declName ctorType
|
| 193 |
+
unless (β isType resultingType) do
|
| 194 |
+
throwUnexpectedResultingTypeNotType resultingType ctorView.declName ctorType
|
| 195 |
+
return type
|
| 196 |
+
let type β elabCtorType
|
| 197 |
+
Term.synthesizeSyntheticMVarsNoPostponing
|
| 198 |
+
let ctorParams β Term.addAutoBoundImplicits ctorParams (ctorView.declId.getTailPos? (canonicalOnly := true))
|
| 199 |
+
let except (mvarId : MVarId) := ctorParams.any fun ctorParam => ctorParam.isMVar && ctorParam.mvarId! == mvarId
|
| 200 |
+
/-
|
| 201 |
+
We convert metavariables in the resulting type into extra parameters. Otherwise, we would not be able to elaborate
|
| 202 |
+
declarations such as
|
| 203 |
+
```
|
| 204 |
+
inductive Palindrome : List Ξ± β Prop where
|
| 205 |
+
| nil : Palindrome [] -- We would get an error here saying "failed to synthesize implicit argument" at `@List.nil ?m`
|
| 206 |
+
| single : (a : Ξ±) β Palindrome [a]
|
| 207 |
+
| sandwich : (a : Ξ±) β Palindrome as β Palindrome ([a] ++ as ++ [a])
|
| 208 |
+
```
|
| 209 |
+
We used to also collect unassigned metavariables on `ctorParams`, but it produced counterintuitive behavior.
|
| 210 |
+
For example, the following declaration used to be accepted.
|
| 211 |
+
```
|
| 212 |
+
inductive Foo
|
| 213 |
+
| bar (x)
|
| 214 |
+
|
| 215 |
+
#check Foo.bar
|
| 216 |
+
-- @Foo.bar : {x : Sort u_1} β x β Foo
|
| 217 |
+
```
|
| 218 |
+
which is also inconsistent with the behavior of auto implicits in definitions. For example, the following example was never accepted.
|
| 219 |
+
```
|
| 220 |
+
def bar (x) := 1
|
| 221 |
+
```
|
| 222 |
+
-/
|
| 223 |
+
let extraCtorParams β Term.collectUnassignedMVars (β instantiateMVars type) #[] except
|
| 224 |
+
trace[Elab.inductive] "extraCtorParams: {extraCtorParams}"
|
| 225 |
+
/- We must abstract `extraCtorParams` and `ctorParams` simultaneously to make
|
| 226 |
+
sure we do not create auxiliary metavariables. -/
|
| 227 |
+
let type β mkForallFVars (extraCtorParams ++ ctorParams) type
|
| 228 |
+
let type β reorderCtorArgs type
|
| 229 |
+
let type β mkForallFVars params type
|
| 230 |
+
trace[Elab.inductive] "{ctorView.declName} : {type}"
|
| 231 |
+
return { name := ctorView.declName, type }
|
| 232 |
+
where
|
| 233 |
+
/--
|
| 234 |
+
Ensures that the arguments to recursive occurrences of the type family being defined match the
|
| 235 |
+
parameters from the inductive definition.
|
| 236 |
+
-/
|
| 237 |
+
checkParamOccs (ctorType : Expr) : MetaM Expr :=
|
| 238 |
+
let visit (e : Expr) : StateT (List Expr) MetaM TransformStep := do
|
| 239 |
+
let f := e.getAppFn
|
| 240 |
+
if indFVars.contains f then
|
| 241 |
+
let mut args := e.getAppArgs
|
| 242 |
+
-- Prefer throwing an "argument mismatch" error rather than a "missing parameter" one
|
| 243 |
+
for i in [:min args.size params.size] do
|
| 244 |
+
let param := params[i]!
|
| 245 |
+
let arg := args[i]!
|
| 246 |
+
unless (β isDefEq param arg) do
|
| 247 |
+
let (arg, param) β addPPExplicitToExposeDiff arg param
|
| 248 |
+
let msg := m!"Mismatched inductive type parameter in{indentExpr e}\nThe provided argument\
|
| 249 |
+
{indentExpr arg}\nis not definitionally equal to the expected parameter{indentExpr param}"
|
| 250 |
+
let noteMsg := m!"The value of parameter '{param}' must be fixed throughout the inductive \
|
| 251 |
+
declaration. Consider making this parameter an index if it must vary."
|
| 252 |
+
throwNamedError lean.inductiveParamMismatch (msg ++ .note noteMsg)
|
| 253 |
+
args := args.set! i param
|
| 254 |
+
unless args.size β₯ params.size do
|
| 255 |
+
let expected := mkAppN f params
|
| 256 |
+
let containingExprMsg := (β get).head?.map toMessageData |>.getD m!"<missing>"
|
| 257 |
+
let msg :=
|
| 258 |
+
m!"Missing parameter(s) in occurrence of inductive type: In the expression{indentD containingExprMsg}\n\
|
| 259 |
+
found{indentExpr e}\nbut expected all parameters to be specified:{indentExpr expected}"
|
| 260 |
+
let noteMsg :=
|
| 261 |
+
m!"All occurrences of an inductive type in the types of its constructors must specify its \
|
| 262 |
+
fixed parameters. Only indices can be omitted in a partial application of the type constructor."
|
| 263 |
+
throwNamedError lean.inductiveParamMissing (msg ++ .note noteMsg)
|
| 264 |
+
return TransformStep.done (mkAppN f args)
|
| 265 |
+
else
|
| 266 |
+
modify fun es => e :: es
|
| 267 |
+
return .continue
|
| 268 |
+
let popContainingExpr (e : Expr) : StateT (List Expr) MetaM TransformStep := do
|
| 269 |
+
modify fun es => es.drop 1
|
| 270 |
+
return .done e
|
| 271 |
+
transform ctorType (pre := visit) (post := popContainingExpr) |>.run' [ctorType]
|
| 272 |
+
|
| 273 |
+
throwUnexpectedResultingTypeMismatch (resultingType indFVar : Expr) (declName : Name) (ctorType : Syntax) := do
|
| 274 |
+
let lazyAppMsg := MessageData.ofLazyM do
|
| 275 |
+
if let some fvarId := indFVar.fvarId? then
|
| 276 |
+
if let some decl := (β getLCtx).find? fvarId then
|
| 277 |
+
if (β whnfD decl.type).isForall then
|
| 278 |
+
return m!" an application of"
|
| 279 |
+
return m!""
|
| 280 |
+
throwNamedErrorAt ctorType lean.ctorResultingTypeMismatch "Unexpected resulting type for constructor '{declName}': \
|
| 281 |
+
Expected{lazyAppMsg}{indentExpr indFVar}\nbut found{indentExpr resultingType}"
|
| 282 |
+
|
| 283 |
+
throwUnexpectedResultingTypeNotType (resultingType : Expr) (declName : Name) (ctorType : Syntax) := do
|
| 284 |
+
let lazyMsg := MessageData.ofLazyM do
|
| 285 |
+
let resultingTypeType β inferType resultingType
|
| 286 |
+
return indentExpr resultingTypeType
|
| 287 |
+
throwNamedErrorAt ctorType lean.ctorResultingTypeMismatch "Unexpected resulting type for constructor '{declName}': \
|
| 288 |
+
Expected a type, but found{indentExpr resultingType}\nof type{lazyMsg}"
|
| 289 |
+
|
| 290 |
+
@[builtin_inductive_elab Lean.Parser.Command.inductive, builtin_inductive_elab Lean.Parser.Command.classInductive]
|
| 291 |
+
def elabInductiveCommand : InductiveElabDescr where
|
| 292 |
+
mkInductiveView (modifiers : Modifiers) (stx : Syntax) := do
|
| 293 |
+
let view β inductiveSyntaxToView modifiers stx
|
| 294 |
+
return {
|
| 295 |
+
view
|
| 296 |
+
elabCtors := fun rs r params => do
|
| 297 |
+
let ctors β elabCtors (rs.map (Β·.indFVar)) params r
|
| 298 |
+
return { ctors }
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
end Lean.Elab.Command
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Elab/InfoTree.lean
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2020 Wojciech Nawrocki. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
|
| 5 |
+
Authors: Wojciech Nawrocki, Leonardo de Moura, Sebastian Ullrich
|
| 6 |
+
-/
|
| 7 |
+
prelude
|
| 8 |
+
import Lean.Elab.InfoTree.Types
|
| 9 |
+
import Lean.Elab.InfoTree.Main
|