/-!
# Tests of structure parameter binder updates
-/

/-!
Motivating issue: https://github.com/leanprover/lean4/issues/3574
Normally one defines a `cast_eq_zero_iff'` field and restates a `cast_eq_zero_iff` version.
-/
namespace Issue3574

class AddMonoidWithOne (R : Type u) extends Add R, Zero R where
  natCast : Nat → R

instance [AddMonoidWithOne R] : Coe Nat R where
  coe := AddMonoidWithOne.natCast
attribute [coe] AddMonoidWithOne.natCast

class CharP [AddMonoidWithOne R] (p : Nat) : Prop where
  cast_eq_zero_iff (R) (p) : ∀ x : Nat, (x : R) = 0 ↔ p ∣ x

-- Both `R` and `p` are explicit now.
/--
info: Issue3574.CharP.cast_eq_zero_iff.{u_1} (R : Type u_1) {inst✝ : AddMonoidWithOne R} (p : Nat) [self : CharP p]
  (x : Nat) : ↑x = 0 ↔ p ∣ x
-/
#guard_msgs in #check CharP.cast_eq_zero_iff

-- Multiple parameters can be updated at once.
class CharP' [AddMonoidWithOne R] (p : Nat) : Prop where
  cast_eq_zero_iff (R p) : ∀ x : Nat, (x : R) = 0 ↔ p ∣ x

/--
info: Issue3574.CharP'.cast_eq_zero_iff.{u_1} (R : Type u_1) {inst✝ : AddMonoidWithOne R} (p : Nat) [self : CharP' p]
  (x : Nat) : ↑x = 0 ↔ p ∣ x
-/
#guard_msgs in #check CharP'.cast_eq_zero_iff

end Issue3574

/-!
Basic test for structures.
-/
namespace Ex1

structure Inhabited (α : Type) where
  default : α

/-- info: Ex1.Inhabited.default {α : Type} (self : Inhabited α) : α -/
#guard_msgs in #check Inhabited.default

structure Inhabited' (α : Type) where
  default (α) : α

/-- info: Ex1.Inhabited'.default (α : Type) (self : Inhabited' α) : α -/
#guard_msgs in #check Inhabited'.default

end Ex1

/-!
Basic test for classes.
-/
namespace Ex2

class Inhabited (α : Type) where
  default : α

/-- info: Ex2.Inhabited.default {α : Type} [self : Inhabited α] : α -/
#guard_msgs in #check Inhabited.default

class Inhabited' (α : Type) where
  default (α) : α

/-- info: Ex2.Inhabited'.default (α : Type) [self : Inhabited' α] : α -/
#guard_msgs in #check Inhabited'.default

end Ex2

/-!
Example with a parameter from a `variable`
-/
namespace Ex3

class Inhabited (α : Type) where
  default : α

/-- info: Ex3.Inhabited.default {α : Type} [self : Inhabited α] : α -/
#guard_msgs in #check Inhabited.default

class Inhabited' (α : Type) where
  default (α) : α

/-- info: Ex3.Inhabited'.default (α : Type) [self : Inhabited' α] : α -/
#guard_msgs in #check Inhabited'.default

end Ex3

/-!
Trying to set a `variable` binder kind; only parameters in the declaration itself can be overridden.
Rationale: we found in mathlib that often users had large binder lists declared at the beginning of files,
and the structure fields accidentally were shadowing them.
-/
namespace Ex4

variable (α : Type)

/--
error: Only parameters appearing in the declaration header may have their binders kinds be overridden

Hint: If this is not intended to be an override, use a binder with a type: for example, `(x : _)`
-/
#guard_msgs in
class Inhabited where
  default (α) : α

end Ex4

/-!
Here, `(α β)` is not an override since `β` is not an existing parameter, so `α` is treated as a binder.
-/
namespace Ex5
/-- error: Failed to infer type of binder `α` -/
#guard_msgs in
class C (α : Type) where
  f (α β) : β
end Ex5

/-!
Here, `(α β)` is not an override since `β` is a field.
-/
namespace Ex6
/-- error: Failed to infer type of binder `α` -/
#guard_msgs in
class C (α : Type) where
  β : Type
  f (α β) : β
end Ex6

/-!
## Constructor updates
-/

/-!
Comparing natural constructor binder kinds to the updated ones.
-/
namespace ExC1

structure WithLp (p : Nat) (V : Type) where toLp ::
  ofLp : V

/-- info: constructor ExC1.WithLp.toLp : {p : Nat} → {V : Type} → V → WithLp p V -/
#guard_msgs in #print WithLp.toLp

structure WithLp' (p : Nat) (V : Type) where toLp (p) ::
  ofLp : V

/-- info: constructor ExC1.WithLp'.toLp : (p : Nat) → {V : Type} → V → WithLp' p V -/
#guard_msgs in #print WithLp'.toLp
end ExC1

/-!
Checking errors
-/
namespace ExC2

/-- error: Expecting binders that update binder kinds of type parameters. -/
#guard_msgs in
structure WithLp (p : Nat) (V : Type) where toLp (p : _) ::
  ofLp : V

variable (n : Nat)

/--
error: Only parameters appearing in the declaration header may have their binders kinds be overridden

Hint: If this is not intended to be an override, use a binder with a type: for example, `(x : _)`
-/
#guard_msgs in
structure WithLp (p : Nat) (V : Type) where toLp (n) ::
  ofLp : V

/-!
Even if `n` is used by `ofLp`, the restriction is still in place.
Motivation 1: the fields themselves have the same restriction, so it's for consistency.
Motivation 2: we should be able to tell whether the param binder update is legit without looking at all the fields.
Motivation 3: the constructor type is constructed before we know which `variable`s will be included. That would take participation of MutualInductive.
-/
/--
error: Only parameters appearing in the declaration header may have their binders kinds be overridden

Hint: If this is not intended to be an override, use a binder with a type: for example, `(x : _)`
-/
#guard_msgs in
structure WithLp (p : Nat) (V : Type) where toLp (n) ::
  ofLp : Fin n → V

/-- error: Expecting binders that update binder kinds of type parameters. -/
#guard_msgs in
structure WithLp (p : Nat) (V : Type) where toLp (m : Int) ::
  ofLp : V

end ExC2
