lean4-htt/tests/lean/run/clear_value.lean
Kyle Miller 02c8c2f9e1
feat: use nondep flag in Expr.letE and LocalContext.ldecl (#8804)
This PR implements first-class support for nondependent let expressions
in the elaborator; recall that a let expression `let x : t := v; b` is
called *nondependent* if `fun x : t => b` typechecks, and the notation
for a nondependent let expression is `have x := v; b`. Previously we
encoded `have` using the `letFun` function, but now we make use of the
`nondep` flag in the `Expr.letE` constructor for the encoding. This has
been given full support throughout the metaprogramming interface and the
elaborator. Key changes to the metaprogramming interface:
- Local context `ldecl`s with `nondep := true` are generally treated as
`cdecl`s. This is because in the body of a `have` expression the
variable is opaque. Functions like `LocalDecl.isLet` by default return
`false` for nondependent `ldecl`s. In the rare case where it is needed,
they take an additional optional `allowNondep : Bool` flag (defaults to
`false`) if the variable is being processed in a context where the value
is relevant.
- Functions such as `mkLetFVars` by default generalize nondependent let
variables and create lambda expressions for them. The
`generalizeNondepLet` flag (default true) can be set to false if `have`
expressions should be produced instead. **Breaking change:** Uses of
`letLambdaTelescope`/`mkLetFVars` need to use `generalizeNondepLet :=
false`. See the next item.
- There are now some mapping functions to make telescoping operations
more convenient. See `mapLetTelescope` and `mapLambdaLetTelescope`.
There is also `mapLetDecl` as a counterpart to `withLetDecl` for
creating `let`/`have` expressions.
- Important note about the `generalizeNondepLet` flag: it should only be
used for variables in a local context that the metaprogram "owns". Since
nondependent let variables are treated as constants in most cases, the
`value` field might refer to variables that do not exist, if for example
those variables were cleared or reverted. Using `mapLetDecl` is always
fine.
- The simplifier will cache its let dependence calculations in the
nondep field of let expressions.
- The `intro` tactic still produces *dependent* local variables. Given
that the simplifier will transform lets into haves, it would be
surprising if that would prevent `intro` from creating a local variable
whose value cannot be used.

Note that nondependence of lets is not checked by the kernel. To
external checker authors: If the elaborator gets the nondep flag wrong,
we consider this to be an elaborator error. Feel free to typecheck `letE
n t v b true` as if it were `app (lam n t b default) v` and please
report issues.

This PR follows up from #8751, which made sure the nondep flag was
preserved in the C++ interface.
2025-06-22 21:54:57 +00:00

380 lines
6.7 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/-!
# Tests for `clear_value` tactic
-/
/-!
Check that `clear_value` clears the value.
-/
/--
trace: x : Nat
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value x
trace_state
exact Nat.zero_le _
/-!
Check that `clear_value` preserves the order of the local context.
-/
/--
trace: x : Nat
y : Nat := 23
⊢ True
-/
#guard_msgs in
example : let _x := 22; let _y := 23; True := by
intro x y
clear_value x
trace_state
trivial
/-!
Test `*` mode.
-/
/--
trace: x : Nat
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value *
trace_state
exact Nat.zero_le _
/-!
Double `*` is not allowed.
-/
/-- error: Multiple `*` arguments provided. -/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value * *
/-!
Check that `clear_value` fails if there is no value to clear.
-/
/-- error: Hypothesis `x` is not a local definition. -/
#guard_msgs in
example (x : Nat) : 0 ≤ x := by
clear_value x
/-!
Check that `clear_value *` fails if it does nothing.
-/
/--
error: Tactic `clear_value` failed to clear any values.
x : Nat
⊢ 0 ≤ x
-/
#guard_msgs in
example (x : Nat) : 0 ≤ x := by
clear_value *
/-!
Check for validation that hypotheses aren't mentioned multiple times.
-/
/-- error: Hypothesis `x` appears multiple times. -/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value x x
/-!
Check that `clear_value (h : x = _)` creates an equality hypothesis.
-/
/--
trace: x : Nat
h : x = 22
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (h : x = _)
trace_state
rw [h]
exact Nat.zero_le _
/-!
Can use any term that is definitionally equal to the term.
-/
/--
trace: x : Nat
h : x = 12 + 10
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (h : x = 12 + 10)
trace_state
rw [h]
exact Nat.zero_le _
/-!
There is no restriction on the value, except that it can't depend on the values being cleared.
-/
/--
trace: x : Nat
h : x = x
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (h : x = x)
trace_state
rw [h]
exact Nat.zero_le _
/-!
Failure because it depends on the value.
-/
/--
error: Tactic `clear_value` failed, the value of `x` cannot be cleared.
x : Nat := 22
h : x = 22 + ↑0
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (h : x = 22 + (0 : Fin x))
trace_state
rw [h]
exact Nat.zero_le _
/-!
Unifies the term.
-/
/--
trace: x : Nat
h : x = id 22
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (h : x = id _)
trace_state
rw [h]
exact Nat.zero_le _
/-!
Error if provided term is not defeq.
-/
/--
error: Provided term
23
is not definitionally equal to
x := 22
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (h : x = 23)
/-!
Error if unification does not solve all metavariables.
-/
/--
error: don't know how to synthesize placeholder for argument 'e'
context:
x : Nat := ⋯
⊢ Nat
---
error: unsolved goals
x : Nat := 22
⊢ 0 ≤ x
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (h : x = if True then _ else _)
/-!
Check `clear_value` with `_` for binder for equality hypothesis.
-/
/--
trace: x : Nat
h✝ : x = 22
⊢ 0 ≤ 22
-/
#guard_msgs in
example : let x := 22; 0 ≤ x := by
intro x
clear_value (_ : x = _)
rw [x = 22]
trace_state
exact Nat.zero_le _
/-!
Check that `clear_value` checks for type correctness.
-/
/--
error: Tactic `clear_value` failed, the value of `x` cannot be cleared.
x : Nat := 22
y : Fin x := 0
⊢ ↑y < x
-/
#guard_msgs in
example : let x := 22; let y : Fin x := 0; y.1 < x := by
intro x y
clear_value x
/-!
Even though we cannot clear `x` on its own, we can clear it if we clear `y` first.
-/
/--
trace: x : Nat
y : Fin x
⊢ ↑y < x
-/
#guard_msgs in
example : let x := 22; let y : Fin x := 0; y.1 < x := by
intro x y
clear_value y
clear_value x
trace_state
exact y.2
/-!
We can clear the values `x` and `y` in any order.
-/
/--
trace: x : Nat
y : Fin x
⊢ ↑y < x
-/
#guard_msgs in
example : let x := 22; let y : Fin x := 0; y.1 < x := by
intro x y
clear_value y x
trace_state
exact y.2
/--
trace: x : Nat
y : Fin x
⊢ ↑y < x
-/
#guard_msgs in
example : let x := 22; let y : Fin x := 0; y.1 < x := by
intro x y
clear_value x y
trace_state
exact y.2
/-!
Clearing `x` on its own and `y` on its own are OK because `y + 1` is nonzero.
-/
/--
trace: x y : Nat
z : Fin (y + 1) := 0
⊢ ↑z < y + 1
-/
#guard_msgs in
example : let x := 22; let y : Nat := x; let z : Fin (y + 1) := 0; z.1 < y + 1 := by
intro x y z
clear_value x
clear_value y
trace_state
exact z.2
/-!
Dependence, `clear_value` fails.
-/
/--
error: Tactic `clear_value` failed, the value of `α` cannot be cleared.
α : Type := Nat
x : α := 1
⊢ x = x
-/
#guard_msgs in
example : let α := Nat; let x : α := 1; @Eq α x x := by
intro α x
clear_value α
/-!
`clear_value *` manages to clear `x` but not `α`
-/
/--
trace: α : Type := Nat
x : α
⊢ x = 1
---
warning: declaration uses 'sorry'
-/
#guard_msgs in
example : let α := Nat; let x : α := 1; @Eq α x 1 := by
intro α x
clear_value *
trace_state
sorry
/-!
`clear_value *` fails if `x` is already cleared
-/
/--
error: Tactic `clear_value` failed to clear any values.
α : Type := Nat
x : α
⊢ x = 1
-/
#guard_msgs in
example : let α := Nat; let x : α := 1; @Eq α x 1 := by
intro α x
clear_value x
clear_value *
/-!
Can use `*` along with `(h : x = _)`.
-/
#guard_msgs in
example : let α := Nat; let x : α := 1; @Eq α x 1 := by
intro α x
clear_value (h : x = _) *
exact h
/-!
Minimized from https://github.com/leanprover-community/mathlib4/issues/25053
The issue is that `by exact k` under the binder creates a delayed assigned metavariable
```
?m :
let val := 22;
val = 22 →
```
that is applied to `val` and `val_def`. Note that this first `` corresponds to `val`,
so, despite the fact that the local context of the metavariable delayed assigned to `?m`
has `val` as a let binding, the value is still being passed in (the types are *not* ensuring
that `val` is defeq to the expected `val`!)
In this case, we can fix the issue by making sure to instantiate metavariables before running
`isTypeCorrect`.
-/
example : True := by
let val := 22
have val_def : val = 22 := rfl
have : Nat.zero = (fun k => by exact k) Nat.zero := by
clear_value val -- used to fail
rfl
trivial
/-!
Local context order preservation.
-/
/--
trace: x : Nat
y : Nat := 2
z : Nat := 3
⊢ x + y = x + z - 1
-/
#guard_msgs in
example : let x := 1; let y := 2; let z := 3; x + y = x + z - 1 := by
intro x y z
clear_value x
trace_state
rfl