File: Mathlib/Tactic/TermCongr.lean

full_name: Mathlib.Tactic.TermCongr.initFn._@.Mathlib.Tactic.TermCongr._hyg.7
code: initialize registerTraceClass `Elab.congr
goal: IO Unit

full_name: Mathlib.Tactic.TermCongr.termCongr
code: /--
`congr(expr)` generates an congruence from an expression containing
congruence holes of the form `$h` or `$(h)`.
In these congruence holes, `h : a = b` indicates that, in the generated congruence,
on the left-hand side `a` is substituted for `$h`
and on the right-hand side `b` is substituted for `$h`.

For example, if `h : a = b` then `congr(1 + $h) : 1 + a = 1 + b`.

This is able to make use of the expected type, for example `(congr(_ + $h) : 1 + _ = _)`
with `h : x = y` gives `1 + x = 1 + y`.
The expected type can be an `Iff`, `Eq`, or `HEq`.
If there is no expected type, then it generates an equality.

Note: the process of generating a congruence lemma involves elaborating the pattern
using terms with attached metadata and a reducible wrapper.
We try to avoid doing so, but these terms can leak into the local context through unification.
This can potentially break tactics that are sensitive to metadata or reducible functions.
Please report anything that goes wrong with `congr(...)` lemmas on Zulip.

For debugging, you can set `set_option trace.Elab.congr true`.
-/
syntax (name := termCongr) "congr(" withoutForbidden(ppDedentIfGrouped(term)) ")" : term
goal: Lean.ParserDescr

full_name: _private.Mathlib.Tactic.TermCongr.0.Mathlib.Tactic.TermCongr.congrHoleForLhsKey
code: /-- Key for congruence hole metadata.
For a `Bool` recording whether this hole is for the LHS elaboration. -/
private def congrHoleForLhsKey : Name 
goal: Lean.Name

full_name: _private.Mathlib.Tactic.TermCongr.0.Mathlib.Tactic.TermCongr.congrHoleIndex
code: /-- Key for congruence hole metadata.
For a `Nat` recording how old this congruence hole is, to prevent reprocessing them
if they leak into the local context. -/
private def congrHoleIndex : Name 
goal: Lean.Name

full_name: Mathlib.Tactic.TermCongr.cHole
code: /-- For holding onto the hole's value along with the value of either the LHS or RHS of the hole.
These occur wrapped in metadata so that they always appear as function application
with exactly four arguments.

Note that there is no relation between `val` and the proof.
We need to decouple these to support letting the proof's elaboration be deferred until
we know whether we want an iff, eq, or heq, while also allowing it to choose
to elaborate as an iff, eq, or heq.
Later, the congruence generator handles any discrepencies.
See `Mathlib.Tactic.TermCongr.CongrResult`. -/
@[reducible, nolint unusedArguments]
def cHole {α : Sort u} (val : α) {p : Prop} (_pf : p) : α 
goal: α

full_name: Mathlib.Tactic.TermCongr.unexpandCHole
code: /-- For error reporting purposes, make the hole pretty print as its value.
We can still see that it is a hole in the info view on mouseover. -/
@[app_unexpander cHole] def unexpandCHole : Lean.PrettyPrinter.Unexpander
  
goal: Lean.PrettyPrinter.Unexpander

full_name: Mathlib.Tactic.TermCongr.mkCHole
code: /-- Create the congruence hole. Used by `elabCHole`.

Saves the current mvarCounter as a proxy for age. We use this to avoid
reprocessing old congruence holes that happened to leak into the local context. -/
def mkCHole (forLhs : Bool) (val pf : Expr) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.cHole?
code: /-- If the expression is a congruence hole, returns `(forLhs, sideVal, pf)`.
If `mvarCounterSaved?` is not none, then only returns the hole if it is at least as recent. -/
def cHole? (e : Expr) (mvarCounterSaved? : Option Nat := none) : Option (Bool × Expr × Expr) 
goal: Option (Bool × Lean.Expr × Lean.Expr)

full_name: Mathlib.Tactic.TermCongr.hasCHole
code: /-- Returns any subexpression that is a recent congruence hole.  -/
def hasCHole (mvarCounterSaved : Nat) (e : Expr) : Option Expr 
goal: Option Lean.Expr

full_name: Mathlib.Tactic.TermCongr.removeCHoles
code: /-- Eliminate all congruence holes from an expression by replacing them with their values. -/
def removeCHoles (e : Expr) : Expr 
goal: Lean.Expr

full_name: Mathlib.Tactic.TermCongr.elabCHole
code: /-- Elaborates a congruence hole and returns either the left-hand side or the right-hand side,
annotated with information necessary to generate a congruence lemma. -/
def elabCHole (h : Syntax) (forLhs : Bool) (expectedType? : Option Expr) : Term.TermElabM Expr 
goal: Lean.Elab.TermElabM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.cHoleExpand
code: /-- (Internal for `congr(...)`)
Elaborates to an expression satisfying `cHole?` that equals the LHS or RHS of `h`,
if the LHS or RHS is available after elaborating `h`. Uses the expected type as a hint. -/
syntax (name := cHoleExpand) "cHole% " (&"lhs" <|> &"rhs") term : term
goal: Lean.ParserDescr

full_name: Mathlib.Tactic.TermCongr.elabCHoleExpand
code: @[term_elab cHoleExpand, inherit_doc cHoleExpand]
def elabCHoleExpand : Term.TermElab 
goal: Lean.Elab.Term.TermElab

full_name: Mathlib.Tactic.TermCongr.processAntiquot
code: /-- Replace all `term` antiquotations in a term using the given `expand` function. -/
def processAntiquot (t : Term) (expand : Term → Term.TermElabM Term) : Term.TermElabM Term 
goal: Lean.Elab.TermElabM Lean.Term

full_name: Mathlib.Tactic.TermCongr.elaboratePattern
code: /-- Given the pattern `t` in `congr(t)`, elaborate it for the given side
by replacing antiquotations with `cHole%` terms, and ensure the elaborated term
is of the expected type. -/
def elaboratePattern (t : Term) (expectedType? : Option Expr) (forLhs : Bool) :
    Term.TermElabM Expr 
goal: Lean.Elab.TermElabM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.mkEqForExpectedType
code: /-- Ensures the expected type is an equality. Returns the equality.
The returned expression satisfies `Lean.Expr.eq?`. -/
def mkEqForExpectedType (expectedType? : Option Expr) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.mkHEqForExpectedType
code: /-- Ensures the expected type is a HEq. Returns the HEq.
This expression satisfies `Lean.Expr.heq?`. -/
def mkHEqForExpectedType (expectedType? : Option Expr) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.mkIffForExpectedType
code: /-- Ensures the expected type is an iff. Returns the iff.
This expression satisfies `Lean.Expr.iff?`. -/
def mkIffForExpectedType (expectedType? : Option Expr) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.ensureIff
code: /-- Make sure that the expected type of `pf` is an iff by unification. -/
def ensureIff (pf : Expr) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrType
code: /-- A request for a type of congruence lemma from a `CongrResult`. -/
inductive CongrType
  | eq | heq
goal: Type

full_name: Mathlib.Tactic.TermCongr.CongrType.heq
code: /-- A request for a type of congruence lemma from a `CongrResult`. -/
inductive CongrType
  | eq | heq
goal: Mathlib.Tactic.TermCongr.CongrType

full_name: Mathlib.Tactic.TermCongr.CongrType.eq
code: /-- A request for a type of congruence lemma from a `CongrResult`. -/
inductive CongrType
  | eq | heq
goal: Mathlib.Tactic.TermCongr.CongrType

full_name: Mathlib.Tactic.TermCongr.CongrResult.rhs
code: /-- A congruence lemma between two expressions.
The proof is generated dynamically, depending on whether the resulting lemma should be
an `Eq` or a `HEq`.
If generating a proof impossible, then the generator can throw an error.
This can be due to either an `Eq` proof being impossible
or due to the lhs/rhs not being defeq to the lhs/rhs of the generated proof,
which can happen for user-supplied congruence holes.

This complexity is to support two features:

1. The user is free to supply Iff, Eq, and HEq lemmas in congurence holes,
   and we're able to transform them into whatever is appropriate for a
   given congruence lemma.
2. If the congrence hole is a metavariable, then we can specialize that
   hole to an Iff, Eq, or HEq depending on what's necessary at that site. -/
structure CongrResult where
  /-- The left-hand side of the congruence result. -/
  lhs : Expr
  /-- The right-hand side of the congruence result. -/
  rhs : Expr
  /-- A generator for an `Eq lhs rhs` or `HEq lhs rhs` proof.
  If such a proof is impossible, the generator can throw an error.
  The inferred type of the generated proof needs only be defeq to `Eq lhs rhs` or `HEq lhs rhs`.
  This function can assign metavariables when constructing the proof.

  If `pf? = none`, then `lhs` and `rhs` are defeq, and the proof is by reflexivity. -/
  (pf? : Option (CongrType → MetaM Expr))
goal: Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.pf?
code: /-- A congruence lemma between two expressions.
The proof is generated dynamically, depending on whether the resulting lemma should be
an `Eq` or a `HEq`.
If generating a proof impossible, then the generator can throw an error.
This can be due to either an `Eq` proof being impossible
or due to the lhs/rhs not being defeq to the lhs/rhs of the generated proof,
which can happen for user-supplied congruence holes.

This complexity is to support two features:

1. The user is free to supply Iff, Eq, and HEq lemmas in congurence holes,
   and we're able to transform them into whatever is appropriate for a
   given congruence lemma.
2. If the congrence hole is a metavariable, then we can specialize that
   hole to an Iff, Eq, or HEq depending on what's necessary at that site. -/
structure CongrResult where
  /-- The left-hand side of the congruence result. -/
  lhs : Expr
  /-- The right-hand side of the congruence result. -/
  rhs : Expr
  /-- A generator for an `Eq lhs rhs` or `HEq lhs rhs` proof.
  If such a proof is impossible, the generator can throw an error.
  The inferred type of the generated proof needs only be defeq to `Eq lhs rhs` or `HEq lhs rhs`.
  This function can assign metavariables when constructing the proof.

  If `pf? = none`, then `lhs` and `rhs` are defeq, and the proof is by reflexivity. -/
  (pf? : Option (CongrType → MetaM Expr))
goal: Option (Mathlib.Tactic.TermCongr.CongrType → Lean.MetaM Lean.Expr)

full_name: Mathlib.Tactic.TermCongr.CongrResult
code: /-- A congruence lemma between two expressions.
The proof is generated dynamically, depending on whether the resulting lemma should be
an `Eq` or a `HEq`.
If generating a proof impossible, then the generator can throw an error.
This can be due to either an `Eq` proof being impossible
or due to the lhs/rhs not being defeq to the lhs/rhs of the generated proof,
which can happen for user-supplied congruence holes.

This complexity is to support two features:

1. The user is free to supply Iff, Eq, and HEq lemmas in congurence holes,
   and we're able to transform them into whatever is appropriate for a
   given congruence lemma.
2. If the congrence hole is a metavariable, then we can specialize that
   hole to an Iff, Eq, or HEq depending on what's necessary at that site. -/
structure CongrResult where
  /-- The left-hand side of the congruence result. -/
  lhs : Expr
  /-- The right-hand side of the congruence result. -/
  rhs : Expr
  /-- A generator for an `Eq lhs rhs` or `HEq lhs rhs` proof.
  If such a proof is impossible, the generator can throw an error.
  The inferred type of the generated proof needs only be defeq to `Eq lhs rhs` or `HEq lhs rhs`.
  This function can assign metavariables when constructing the proof.

  If `pf? = none`, then `lhs` and `rhs` are defeq, and the proof is by reflexivity. -/
  (pf? : Option (CongrType → MetaM Expr))
goal: Type

full_name: Mathlib.Tactic.TermCongr.CongrResult.lhs
code: /-- A congruence lemma between two expressions.
The proof is generated dynamically, depending on whether the resulting lemma should be
an `Eq` or a `HEq`.
If generating a proof impossible, then the generator can throw an error.
This can be due to either an `Eq` proof being impossible
or due to the lhs/rhs not being defeq to the lhs/rhs of the generated proof,
which can happen for user-supplied congruence holes.

This complexity is to support two features:

1. The user is free to supply Iff, Eq, and HEq lemmas in congurence holes,
   and we're able to transform them into whatever is appropriate for a
   given congruence lemma.
2. If the congrence hole is a metavariable, then we can specialize that
   hole to an Iff, Eq, or HEq depending on what's necessary at that site. -/
structure CongrResult where
  /-- The left-hand side of the congruence result. -/
  lhs : Expr
  /-- The right-hand side of the congruence result. -/
  rhs : Expr
  /-- A generator for an `Eq lhs rhs` or `HEq lhs rhs` proof.
  If such a proof is impossible, the generator can throw an error.
  The inferred type of the generated proof needs only be defeq to `Eq lhs rhs` or `HEq lhs rhs`.
  This function can assign metavariables when constructing the proof.

  If `pf? = none`, then `lhs` and `rhs` are defeq, and the proof is by reflexivity. -/
  (pf? : Option (CongrType → MetaM Expr))
goal: Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.isRfl
code: /-- Returns whether the proof is by reflexivity.
Such congruence proofs are trivial. -/
def CongrResult.isRfl (res : CongrResult) : Bool 
goal: Bool

full_name: Mathlib.Tactic.TermCongr.CongrResult.eq
code: /-- Returns the proof that `lhs = rhs`. Fails if the `CongrResult` is inapplicable.
Throws an error if the `lhs` and `rhs` have non-defeq types.
If `pf? = none`, this returns the `rfl` proof. -/
def CongrResult.eq (res : CongrResult) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.heq
code: /-- Returns the proof that `HEq lhs rhs`. Fails if the `CongrResult` is inapplicable.
If `pf? = none`, this returns the `rfl` proof. -/
def CongrResult.heq (res : CongrResult) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.iff
code: /-- Returns a proof of `lhs ↔ rhs`. Uses `CongrResult.eq`. -/
def CongrResult.iff (res : CongrResult) : MetaM Expr 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.trans
code: /-- Combine two congruence proofs using transitivity.
Does not check that `res1.rhs` is defeq to `res2.lhs`.
If both `res1` and `res2` are trivial then the result is trivial. -/
def CongrResult.trans (res1 res2 : CongrResult) : CongrResult 
goal: Mathlib.Tactic.TermCongr.CongrResult

full_name: Mathlib.Tactic.TermCongr.CongrResult.mk'
code: /-- Make a `CongrResult` from a LHS, a RHS, and a proof of an Iff, Eq, or HEq.
The proof is allowed to have a metavariable for its type.
Validates the inputs and throws errors in the `pf?` function.

The `pf?` function is responsible for finally unifying the type of `pf` with `lhs` and `rhs`. -/
def CongrResult.mk' (lhs rhs : Expr) (pf : Expr) : CongrResult 
goal: Mathlib.Tactic.TermCongr.CongrResult

full_name: Mathlib.Tactic.TermCongr.CongrResult.mk'.ensureSidesDefeq
code: /-- Make a `CongrResult` from a LHS, a RHS, and a proof of an Iff, Eq, or HEq.
The proof is allowed to have a metavariable for its type.
Validates the inputs and throws errors in the `pf?` function.

The `pf?` function is responsible for finally unifying the type of `pf` with `lhs` and `rhs`. -/
def CongrResult.mk' (lhs rhs : Expr) (pf : Expr) : CongrResult 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.mk'.toEqPf
code: /-- Make a `CongrResult` from a LHS, a RHS, and a proof of an Iff, Eq, or HEq.
The proof is allowed to have a metavariable for its type.
Validates the inputs and throws errors in the `pf?` function.

The `pf?` function is responsible for finally unifying the type of `pf` with `lhs` and `rhs`. -/
def CongrResult.mk' (lhs rhs : Expr) (pf : Expr) : CongrResult 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.mk'.toHEqPf
code: /-- Make a `CongrResult` from a LHS, a RHS, and a proof of an Iff, Eq, or HEq.
The proof is allowed to have a metavariable for its type.
Validates the inputs and throws errors in the `pf?` function.

The `pf?` function is responsible for finally unifying the type of `pf` with `lhs` and `rhs`. -/
def CongrResult.mk' (lhs rhs : Expr) (pf : Expr) : CongrResult 
goal: Lean.MetaM Lean.Expr

full_name: Mathlib.Tactic.TermCongr.CongrResult.defeq
code: /-- Force the lhs and rhs to be defeq. For when `dsimp`-like congruence is necessary.
Clears the proof. -/
def CongrResult.defeq (res : CongrResult) : MetaM CongrResult 
goal: Lean.MetaM Mathlib.Tactic.TermCongr.CongrResult

full_name: Mathlib.Tactic.TermCongr.CongrResult.mkDefault
code: /-- Tries to make a congruence between `lhs` and `rhs` automatically.
1. If they are defeq, returns a trivial congruence.
2. Tries using `Subsingleton.elim`.
3. Tries `proof_irrel_heq` as another effort to avoid doing congruence on proofs.
3. Otherwise throws an error.

Note: `mkAppM` uses `withNewMCtxDepth`, which prevents typeclass inference
from accidentally specializing `Sort _` to `Prop`, which could otherwise happen
because there is a `Subsingleton Prop` instance. -/
def CongrResult.mkDefault (lhs rhs : Expr) : MetaM CongrResult 
goal: Lean.MetaM Mathlib.Tactic.TermCongr.CongrResult

full_name: Mathlib.Tactic.TermCongr.CongrResult.mkDefault'
code: /-- Does `CongrResult.mkDefault` but makes sure there are no lingering congruence holes. -/
def CongrResult.mkDefault' (mvarCounterSaved : Nat) (lhs rhs : Expr) : MetaM CongrResult 
goal: Lean.MetaM Mathlib.Tactic.TermCongr.CongrResult

full_name: Mathlib.Tactic.TermCongr.throwCongrEx
code: /-- Throw an internal error. -/
def throwCongrEx {α : Type} (lhs rhs : Expr) (msg : MessageData) : MetaM α 
goal: Lean.MetaM α

full_name: Mathlib.Tactic.TermCongr.mkCongrOfCHole?
code: /-- If `lhs` or `rhs` is a congruence hole, then process it.
Only process ones that are at least as new as `mvarCounterSaved`
since nothing prevents congruence holes from leaking into the local context. -/
def mkCongrOfCHole? (mvarCounterSaved : Nat) (lhs rhs : Expr) : MetaM (Option CongrResult) 
goal: Lean.MetaM (Option Mathlib.Tactic.TermCongr.CongrResult)

full_name: Mathlib.Tactic.TermCongr.mkCongrOf
code: /-- Walks along both `lhs` and `rhs` simultaneously to create a congruence lemma between them.

Where they are desynchronized, we fall back to the base case (using `CongrResult.mkDefault'`)
since it's likely due to unification with the expected type,
from `_` placeholders or implicit arguments being filled in. -/
partial def mkCongrOf (depth : Nat) (mvarCounterSaved : Nat) (lhs rhs : Expr) :
    MetaM CongrResult 
goal: Lean.MetaM Mathlib.Tactic.TermCongr.CongrResult

full_name: Mathlib.Tactic.TermCongr.elabTermCongr
code: @[term_elab termCongr, inherit_doc termCongr]
def elabTermCongr : Term.TermElab 
goal: Lean.Elab.Term.TermElab