fix: mark `Grind.nestedDecidable` as an abbreviation

This PR fixes a performance issue where proof terms produced by `grind` could trigger kernel deterministic timeouts.
`grind` canonicalizes nested `Decidable` instances under the identity wrapper `Grind.nestedDecidable`, leaving the kernel to check
`t =?= Grind.nestedDecidable t`. The kernel does not see the `[reducible]` attribute, and its lazy-delta heuristic unfolded `t`
instead of the wrapper. When `t` is an instance such as `UInt32.decLe` applied to a symbolic argument and a large literal
(e.g., `97 ≤ c.val + 4294967264` coming from `Char`/`UInt32` wraparound), the check descended through `BitVec` and `Fin` into
`Nat.ble` on a `2^32`-sized literal with a free variable inside, where the fast numeral path does not apply, and effectively never terminated.
Marking `nestedDecidable` as an abbreviation stores the `abbrev` reducibility hint in the declaration itself, which the kernel does honor,
so the wrapper side is unfolded first and the check succeeds immediately.

The `unfoldReducible` preprocessor now treats `Grind.nestedDecidable` as a `grind` gadget that must not be unfolded: like `Grind.EqMatch` and `Grind.MatchCond`,
it is an `abbrev` that must survive until the corresponding propagators consume it.

Reported by Paul Reichert.
diff --git a/src/Init/Grind/Util.lean b/src/Init/Grind/Util.lean
index 1e31369..0189c6b 100644
--- a/src/Init/Grind/Util.lean
+++ b/src/Init/Grind/Util.lean
@@ -18,7 +18,9 @@
 /-- A helper gadget for annotating nested decidable instances in goals. -/
 -- Remark: we currently have special gadgets for the two most common subsingletons in Lean, and are the only
 -- currently supported in `grind`. We may add a generic `nestedSubsingleton` inn the future.
-@[expose] def nestedDecidable {p : Prop} (h : Decidable p) : Decidable p := h
+-- **Note**: We mark it as an abbreviation to ensure the kernel will not get confused when checking
+-- that `t =?= Grind.nestedDecidable t`
+abbrev nestedDecidable {p : Prop} (h : Decidable p) : Decidable p := h
 
 /--
 Gadget for marking `match`-expressions that should not be reduced by the `grind` simplifier, but the discriminants should be normalized.
@@ -69,7 +71,9 @@
   subst h; apply HEq.refl
 
 theorem nestedDecidable_congr (p q : Prop) (h : p = q) (hp : Decidable p) (hq : Decidable q) : @nestedDecidable p hp ≍ @nestedDecidable q hq := by
-  subst h; cases hp <;> cases hq <;> simp <;> contradiction
+  subst h; cases hp <;> cases hq <;> unfold nestedDecidable <;> try simp
+  next h1 h2 => exact False.elim (h1 h2)
+  next h1 h2 => exact False.elim (h2 h1)
 
 @[app_unexpander nestedProof]
 meta def nestedProofUnexpander : PrettyPrinter.Unexpander := fun stx => do
diff --git a/src/Lean/Meta/Sym/AlphaShareCommon.lean b/src/Lean/Meta/Sym/AlphaShareCommon.lean
index f3a6039..051b5ae 100644
--- a/src/Lean/Meta/Sym/AlphaShareCommon.lean
+++ b/src/Lean/Meta/Sym/AlphaShareCommon.lean
@@ -55,12 +55,12 @@
 
 /--
 Returns `true` if `declName` is the name of a grind helper declaration that
-should not be unfolded by `unfoldReducible`. `Grind.EqMatch` and `Grind.MatchCond`
+should not be unfolded by `unfoldReducible`. `Grind.EqMatch`, `Grind.MatchCond`, `Grind.nestedReducible`
 are `abbrev`s, but they are gadgets that must survive until the corresponding
 propagators consume them.
 -/
 def isGrindGadget (declName : Name) : Bool :=
-  declName == ``Grind.EqMatch || declName == ``Grind.MatchCond
+  declName == ``Grind.EqMatch || declName == ``Grind.MatchCond || declName == ``Grind.nestedDecidable
 
 /--
 Returns `true` if `declName` is a reducible constant that the `unfoldReducible`
diff --git a/tests/elab/grind_nestedDecidable_kernel_issue.lean b/tests/elab/grind_nestedDecidable_kernel_issue.lean
new file mode 100644
index 0000000..e67a361
--- /dev/null
+++ b/tests/elab/grind_nestedDecidable_kernel_issue.lean
@@ -0,0 +1,40 @@
+module
+set_option warn.sorry false
+/-!
+Paul Reichert's examples: `grind` + kernel interaction through `Grind.nestedDecidable`.
+
+`grind` canonicalizes `Decidable` instances under the identity wrapper
+`Grind.nestedDecidable`, assuming `X =?= nestedDecidable X` is free. The elaborator
+unfolds the reducible wrapper instantly, but the kernel does not take `[reducible]` hint.
+We fix the issue by tagging `Grind.nestedDecidable` as an abbreviation which tags in the
+kernel as an abbreviation.
+-/
+
+set_option maxHeartbeats 1000 -- for the health of your machines
+
+/-!
+
+Grind's literal normalization produces `97 ≤ c.val + 4294967264` and cutsat closes the goal,
+but `decide_eq_true_eq`'s metavariable type check
+```lean
+Decidable (97 ≤ c.val + 4294967264) =?= Decidable ('a'.val ≤ {val := c.val + ('A'.val - 'a'.val), ...}.val)
+```
+fails at implicit transparency because we made `UInt32.toBitVec` semireducible.
+Grind then keeps the `decide` layer, canonicalizes instances under `Grind.nestedDecidable`,
+and leaves it to the kernel to verify that `X =?= nestedDecidable X` is true with
+`X = 'a'.val.decLe (c.val + ('A'.val - 'a'.val))`.
+This leads to the exact problem shown previously.
+-/
+
+section
+set_option allowUnsafeReducibility true
+attribute [local semireducible] UInt32.toBitVec -- or `Char.ofNat`
+
+set_option maxHeartbeats 5000 in
+example (c : Char) : c.toUpper.isLower = false := by
+  simp only [Char.isLower, Char.toUpper]
+  split
+  · grind only  -- (kernel) deterministic timeout
+  · sorry
+
+end