Skip to content

Commit 3ea59e1

Browse files
kim-emclaude
andauthored
fix: set implicitReducible on grandparent subobject projections (#12701)
This PR fixes a gap in how `@[implicit_reducible]` is assigned to parent projections during structure elaboration. When `class C extends P₁, P₂` has diamond inheritance, some ancestor structures become constructor subobject fields even though they aren't direct parents. For example, in `Monoid extends Semigroup, MulOneClass`, `One` becomes a constructor subobject of `Monoid` — its field `one` doesn't overlap with `Semigroup`'s fields, and `inSubobject?` is `none` during `MulOneClass` flattening. `mkProjections` creates the projection `Monoid.toOne` but defers reducibility to `addParentInstances` (guarded by `if !instImplicit`). However, `addParentInstances` only processes direct parents from the `extends` clause. Grandparent subobject projections fall through the gap and stay `semireducible`. This causes defeq failures when `backward.isDefEq.respectTransparency` is enabled (#12179): at `.instances` transparency, the semireducible grandparent projection can't unfold, so two paths to the same ancestor structure aren't recognized as definitionally equal. Fix: before `addParentInstances`, iterate over all `.subobject` fields and set `implicitReducible` on those whose parent is a class. 🤖 Prepared with Claude Code --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d59f229 commit 3ea59e1

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

src/Lean/Elab/Structure.lean

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,16 @@ def elabStructureCommand : InductiveElabDescr where
15491549
if fieldInfo.kind.isInCtor then
15501550
enableRealizationsForConst fieldInfo.declName
15511551
if view.isClass then
1552+
-- Set implicitReducible on subobject projections to class parents.
1553+
-- mkProjections defers reducibility to addParentInstances, but
1554+
-- addParentInstances only handles direct parents. Subobject fields
1555+
-- for non-direct parents (grandparents promoted to constructor
1556+
-- subobjects during diamond flattening) also need implicitReducible
1557+
-- to be unfoldable at .instances transparency.
1558+
for fieldInfo in fieldInfos do
1559+
if let .subobject parentName := fieldInfo.kind then
1560+
if isClass (← getEnv) parentName then
1561+
setReducibilityStatus fieldInfo.declName .implicitReducible
15521562
addParentInstances parentInfos
15531563
-- Add field docstrings here (after @[class] attribute is applied)
15541564
-- so that Verso docstrings can use the class.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Lean
2+
3+
/-!
4+
# Grandparent subobject projections should be `@[implicit_reducible]`
5+
6+
When `class C extends P₁, P₂` has diamond inheritance, some ancestor structures
7+
end up as constructor subobject fields even though they aren't direct parents.
8+
These grandparent projections need `@[implicit_reducible]` so they unfold at
9+
`.instances` transparency.
10+
11+
For example, with `MyMonoid extends MySemigroup, MyMulOneClass` where both share
12+
`MyMul`, the structure `MyOne` becomes a constructor subobject of `MyMonoid`
13+
(it has no overlap with `MySemigroup`). So `MyMonoid.toMyOne` is created by
14+
`mkProjections` as a subobject projection, but it is NOT a direct parent —
15+
it's a grandparent reached through `MyMulOneClass`.
16+
17+
Previously, `addParentInstances` only set `@[implicit_reducible]` on direct
18+
parent projections. This test verifies that grandparent subobject projections
19+
also receive `@[implicit_reducible]`.
20+
-/
21+
22+
-- Minimal hierarchy with a diamond via MyMul
23+
class MyOne (α : Type _) where one : α
24+
class MyMul (α : Type _) where mul : α → α → α
25+
class MyMulOneClass (M : Type _) extends MyOne M, MyMul M
26+
class MySemigroup (G : Type _) extends MyMul G
27+
class MyMonoid (M : Type _) extends MySemigroup M, MyMulOneClass M
28+
29+
open Lean in
30+
def showReducibility (n : Name) : CoreM Unit := do
31+
IO.println s!"{n}: {repr (← getReducibilityStatus n)}"
32+
33+
/--
34+
info: MyMonoid.toMySemigroup: Lean.ReducibilityStatus.implicitReducible
35+
MyMonoid.toMyMulOneClass: Lean.ReducibilityStatus.implicitReducible
36+
MyMonoid.toMyOne: Lean.ReducibilityStatus.implicitReducible
37+
-/
38+
#guard_msgs in
39+
#eval do
40+
showReducibility ``MyMonoid.toMySemigroup
41+
showReducibility ``MyMonoid.toMyMulOneClass
42+
-- Grandparent subobject projection should also be implicitReducible
43+
showReducibility ``MyMonoid.toMyOne

0 commit comments

Comments
 (0)