Skip to content

Commit 4591082

Browse files
kim-emclaude
andcommitted
fix(GCongr): handle @[reducible] HasSSubset.SSubset in hypothesis classification and tactic
When `HasSSubset.SSubset` is `@[reducible]` (from lean4#12368), `whnf` at reducible transparency can destroy the relation structure of `⊂`, making `getRel` unable to parse the result. This fixes three locations: 1. **Attribute registration** (`makeGCongrLemma`): Try `getRel` on the raw hypothesis type before `forallTelescopeReducing` whnfs it. This ensures hypotheses like `h : s ⊂ t` are correctly classified as main subgoals. 2. **Tactic core** (`MVarId.gcongr`): Fall back to the raw goal type when `getRel` fails on the whnf'd form. 3. **Tactic elab** (`gcongr`/`rel`): Same fallback pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 47ac484 commit 4591082

File tree

1 file changed

+26
-6
lines changed

1 file changed

+26
-6
lines changed

Mathlib/Tactic/GCongr/Core.lean

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,17 +271,28 @@ def makeGCongrLemma (hyps : Array Expr) (target : Expr) (declName : Name) (prio
271271
if numVarying = 0 then
272272
fail "LHS and RHS are the same"
273273
let mut mainSubgoals := #[]
274+
let findPair (lhs rhs : FVarId) : Option Bool :=
275+
pairs.findSome? fun pair =>
276+
if (lhs, rhs) == pair then false else if (rhs, lhs) == pair then true else none
274277
let mut i := 0
275278
-- iterate over antecedents `hyp` to the lemma
276279
for hyp in hyps do
277-
mainSubgoals ← forallTelescopeReducing (← inferType hyp) fun args hypTy => do
280+
-- First, try matching the raw (un-whnf'd) hypothesis type as a relation.
281+
-- This avoids unfolding `@[reducible]` class projections (like `HasSSubset.SSubset`)
282+
-- that would destroy the relation structure.
283+
let rawHypTy ← inferType hyp
284+
if let some (_, lhs₁, rhs₁) := getRel rawHypTy then
285+
if let .fvar lhs₁ := lhs₁.getAppFn then
286+
if let .fvar rhs₁ := rhs₁.getAppFn then
287+
if let some isContra := findPair lhs₁ rhs₁ then
288+
mainSubgoals := mainSubgoals.push (i, 0, isContra)
289+
i := i + 1
290+
continue
291+
mainSubgoals ← forallTelescopeReducing rawHypTy fun args hypTy => do
278292
-- pull out the conclusion `hypTy` of the antecedent, and check whether it is of the form
279293
-- `lhs₁ _ ... _ ≈ rhs₁ _ ... _` (for a possibly different relation `≈` than the relation
280294
-- `rel` above)
281295
let hypTy ← whnf hypTy
282-
let findPair (lhs rhs : FVarId) : Option Bool :=
283-
pairs.findSome? fun pair =>
284-
if (lhs, rhs) == pair then false else if (rhs, lhs) == pair then true else none
285296
if let some (_, lhs₁, rhs₁) := getRel hypTy then
286297
if let .fvar lhs₁ := lhs₁.getAppFn then
287298
if let .fvar rhs₁ := rhs₁.getAppFn then
@@ -580,7 +591,11 @@ partial def _root_.Lean.MVarId.gcongr
580591
let depth + 1 := depth | return (false, names, #[g]) -- we know that there is no mdata to remove
581592
-- Check that the goal is of the form `rel (lhsHead _ ... _) (rhsHead _ ... _)`
582593
let rel ← withReducible g.getType'
583-
let some (relName, lhs, rhs) := getRel rel | throwTacticEx `gcongr g m!"{rel} is not a relation"
594+
-- If `whnf` destroyed the relation structure (e.g., by unfolding `@[reducible]` class
595+
-- projections like `HasSSubset.SSubset`), fall back to the raw goal type.
596+
let rel ← if (getRel rel).isSome then pure rel else g.getType
597+
let some (relName, lhs, rhs) := getRel rel
598+
| throwTacticEx `gcongr g m!"{← withReducible g.getType'} is not a relation"
584599
-- If there is a pattern annotation
585600
if let some mdataLhs := mdataLhs? then
586601
let mdataExpr := if mdataLhs then lhs else rhs
@@ -722,6 +737,9 @@ elab "gcongr" template:(ppSpace colGt term)?
722737
let g ← getMainGoal
723738
g.withContext do
724739
let type ← withReducible g.getType'
740+
-- If `whnf` destroyed the relation structure (e.g., by unfolding `@[reducible]` class
741+
-- projections like `HasSSubset.SSubset`), fall back to the raw goal type.
742+
let type ← if (getRel type).isSome then pure type else g.getType
725743
let some (_rel, lhs, _rhs) := getRel type
726744
| throwError "gcongr failed, not a relation"
727745
-- Get the names from the `with x y z` list
@@ -779,7 +797,9 @@ elab_rules : tactic
779797
let g ← getMainGoal
780798
g.withContext do
781799
let hyps ← hyps.getElems.mapM (elabTerm · none)
782-
let some (_rel, lhs, rhs) := getRel (← withReducible g.getType')
800+
let relType ← withReducible g.getType'
801+
let relType ← if (getRel relType).isSome then pure relType else g.getType
802+
let some (_rel, lhs, rhs) := getRel relType
783803
| throwError "rel failed, goal not a relation"
784804
unless ← isDefEq (← inferType lhs) (← inferType rhs) do
785805
throwError "rel failed, goal not a relation"

0 commit comments

Comments
 (0)