import Lean
/-!
# Tests of the various `let` options
-/

set_option linter.unusedVariables false

/-!
No options.
-/
/--
info: let x := true;
!x : Bool
-/
#guard_msgs in #check let x := true; !x

/-!
The `+nondep` option gives `have`.
-/
/--
info: have x := true;
!x : Bool
-/
#guard_msgs in #check let +nondep x := true; !x

/-!
The `-nondep` option on `have` is a `let`.
-/
/--
info: let x := true;
!x : Bool
-/
#guard_msgs in #check have -nondep x := true; !x

/-!
The `+zeta` option.
-/
/-- info: !true : Bool -/
#guard_msgs in #check let +zeta x := true; !x

/-!
The `+usedOnly` option.
-/
/--
info: let x := true;
!x : Bool
-/
#guard_msgs in #check let +usedOnly x := true; !x
/-- info: !false : Bool -/
#guard_msgs in #check let +usedOnly x := true; !false

/-!
`eq` for plain `let`
-/
/--
trace: m : Nat := 1
hyp : m = 1
⊢ True
---
warning: declaration uses 'sorry'
-/
#guard_msgs in
example : True := by
  refine let (eq := hyp) m := 1; ?_
  trace_state
  sorry

/-!
`eq` for `have`
-/
/--
trace: m : Nat
hyp : m = 1
⊢ True
---
warning: declaration uses 'sorry'
-/
#guard_msgs in
example : True := by
  refine have (eq := hyp) m := 1; ?_
  trace_state
  sorry

/-!
Patterns with `(eq := _)`
-/
/--
trace: p : Nat × Nat
x y : Nat
h : p = (x, y)
this : x + y = p.fst + p.snd
⊢ True
-/
#guard_msgs in
example (p : Nat × Nat) : True :=
  let (eq := h) (x, y) := p
  have : x + y = p.1 + p.2 := by
    simp [h]
  by
    trace_state
    trivial
/--
trace: p : Nat × Nat
x y : Nat
h✝ : p = (x, y)
⊢ x + y = p.fst + p.snd
-/
#guard_msgs in
example (p : Nat × Nat) : True :=
  let (eq := _) (x, y) := p
  have : x + y = p.1 + p.2 := by
    trace_state
    simp [*]
  trivial

/-!
`+postponeValue`, example from `Lean.Elab.Term.Do.ToTerm.mkJoinPoint`.
-/
/--
error: Type mismatch
  jp ()
has type
  IO (IO.Ref Bool)
but is expected to have type
  IO Unit
-/
#guard_msgs in
def f (x : Nat) : IO Unit :=
  let jp (u : Unit) : IO _ :=
    IO.mkRef true
  do
    if x > 0 then
      IO.println "not zero"
      jp ()
    else
      jp ()
/--
error: Type mismatch
  IO.mkRef true
has type
  BaseIO (IO.Ref Bool)
but is expected to have type
  IO Unit
-/
#guard_msgs in
def f' (x : Nat) : IO Unit :=
  let +postponeValue jp (u : Unit) : IO _ :=
    IO.mkRef true
  do
    if x > 0 then
      IO.println "not zero"
      jp ()
    else
      jp ()

/-!
Testing `+generalize`
-/
/--
trace: x y z : Nat
⊢ z = y + x
---
warning: declaration uses 'sorry'
-/
#guard_msgs in
example (x y : Nat) : x + y = y + x := by
  refine have +generalize z := x + y; ?_
  trace_state
  sorry
/--
trace: x y z : Nat
h : z = x + y
⊢ z = y + x
-/
#guard_msgs in
example (x y : Nat) : x + y = y + x := by
  refine have +generalize (eq := h) z := x + y; ?_
  trace_state
  rwa [Nat.add_comm] at h

/-!
`+generalize` example with a numeric value.
-/
/--
trace: x y z : Nat
⊢ x ≤ x + z
-/
#guard_msgs in
example (x y : Nat) : x ≤ x + 1 := by
  refine have +generalize z := 1; ?_
  trace_state
  apply Nat.le_add_right

/-!
Motive checking with `+generalize`.
-/
/--
error: failed to elaborate with `+generalize`, generalized expected type is not type correct:
  0 = 0
-/
#guard_msgs in
example (n : Nat) [NeZero n] : (0 : Fin n) = (0 : Fin n) := by
  refine have +generalize z := n; ?_
