Skip to content

Commit d4ddabb

Browse files
committed
feat: make noConfusion even more heterogeneous
This PR makes the noConfusion principles even more heterogeneous, by allowing not just indices but also parameters to be differ. This is a breaking change for manual use of `noConfusion` for types with parameters. Pass suitable `rfl` arguments, and use `eq_of_heq` on the resulting equalities as needed. This fixes #11560.
1 parent 9466a05 commit d4ddabb

File tree

7 files changed

+121
-79
lines changed

7 files changed

+121
-79
lines changed

src/Lean/Meta/Constructions/NoConfusion.lean

Lines changed: 70 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -33,25 +33,27 @@ constructor. In particular, returns
3333
```
3434
fun params x1 x2 x3 x1' x2' x3' => (x1 = x1' → x2 = x2' → x3 = x3' → P)
3535
```
36-
where `x1 x2 x3` and `x1' x2' x3'` are the fields of a constructor application of `ctorName`,
37-
omitting equalities between propositions and using `HEq` where needed.
36+
where `x1 x2 x3` and `x1' x2' x3'` are the parameters and fields of a constructor application of
37+
`ctorName`, omitting equalities between propositions and using `HEq` where needed.
3838
-/
3939
def mkNoConfusionCtorArg (ctorName : Name) (P : Expr) : MetaM Expr := do
4040
let ctorInfo ← getConstInfoCtor ctorName
4141
-- We bring the constructor's parameters into scope abstractly, this way
4242
-- we can check if we need to use HEq. (The concrete fields could allow Eq)
43-
forallBoundedTelescope ctorInfo.type ctorInfo.numParams fun xs t => do
44-
forallTelescopeReducing t fun fields1 _ => do
45-
forallTelescopeReducing t fun fields2 _ => do
46-
withPrimedNames fields2 do
43+
forallBoundedTelescope ctorInfo.type ctorInfo.numParams fun xs1 t1 => do
44+
forallTelescopeReducing t1 fun fields1 _ => do
45+
forallBoundedTelescope ctorInfo.type ctorInfo.numParams fun xs2 t2 => do
46+
withPrimedNames xs2 do
47+
forallTelescopeReducing t2 fun fields2 _ => do
48+
withPrimedNames fields2 do
4749
let mut t := P
4850
for f1 in fields1.reverse, f2 in fields2.reverse do
4951
if (← isProof f1) then
5052
continue
5153
let name := (← f1.fvarId!.getUserName).appendAfter "_eq"
5254
let eq ← mkEqHEq f1 f2
5355
t := mkForall name .default eq t
54-
mkLambdaFVars (xs ++ fields1 ++ fields2) t
56+
mkLambdaFVars (xs1 ++ fields1 ++ xs2 ++ fields2) t
5557

5658
register_builtin_option backward.linearNoConfusionType : Bool := {
5759
defValue := true
@@ -89,54 +91,57 @@ def mkNoConfusionType (indName : Name) : MetaM Unit := do
8991
let v::us := casesOnInfo.levelParams.map mkLevelParam | panic! "unexpected universe levels on `casesOn`"
9092
let e := mkConst casesOnName (v.succ::us)
9193
let t ← inferType e
92-
let e ← forallBoundedTelescope t info.numParams fun xs t => do
93-
let e := mkAppN e xs
94-
let PType := mkSort v
95-
withLocalDeclD `P PType fun P => do
96-
let motive ← forallTelescope (← whnfD t).bindingDomain! fun ys _ =>
97-
mkLambdaFVars ys PType
98-
let ti ← instantiateForall t #[motive]
99-
let e := mkApp e motive
100-
forallBoundedTelescope ti (some (info.numIndices + 1)) fun ysx1 t => do -- indices and major
101-
forallBoundedTelescope ti (some (info.numIndices + 1)) fun ysx2 _ => do -- indices and major
94+
let PType := mkSort v
95+
let e ← withLocalDeclD `P PType fun P => do
96+
forallBoundedTelescope t info.numParams fun xs1 t1 => do
97+
forallBoundedTelescope t info.numParams fun xs2 t2 => do
98+
withPrimedNames xs2 do
99+
let e := mkAppN e xs1
100+
let motive1 ← forallTelescope (← whnfD t1).bindingDomain! fun ys _ => mkLambdaFVars ys PType
101+
let t1 ← instantiateForall t1 #[motive1]
102+
let e := mkApp e motive1
103+
let motive2 ← forallTelescope (← whnfD t2).bindingDomain! fun ys _ => mkLambdaFVars ys PType
104+
let t2 ← instantiateForall t2 #[motive2]
105+
forallBoundedTelescope t1 (some (info.numIndices + 1)) fun ysx1 t1 => do -- indices and major
106+
forallBoundedTelescope t2 (some (info.numIndices + 1)) fun ysx2 _t2 => do -- indices and major
102107
withPrimedNames ysx2 do
103108
let e := mkAppN e ysx1
104-
let altTypes ← arrowDomainsN info.numCtors t
109+
let altTypes ← arrowDomainsN info.numCtors t1
105110
let alts ← altTypes.mapIdxM fun i altType => do
106111
forallTelescope altType fun zs1 _ => do
107112
if useLinearConstruction then
108-
let ctorIdxApp := mkAppN (mkConst (mkCtorIdxName indName) us) (xs ++ ysx2)
113+
let ctorIdxApp := mkAppN (mkConst (mkCtorIdxName indName) us) (xs2 ++ ysx2)
109114
let alt ← mkIfNatEq PType (ctorIdxApp) (mkRawNatLit i)
110115
else» := fun _ => pure P) fun h => do
111116
let conName := info.ctors[i]!
112117
let withName := mkConstructorElimName indName conName
113118
let e := mkConst withName (v.succ :: us)
114-
let e := mkAppN e (xs ++ #[motive] ++ ysx2 ++ #[h])
119+
let e := mkAppN e (xs2 ++ #[motive2] ++ ysx2 ++ #[h])
115120
let e := mkApp e <|
116121
← forallTelescopeReducing ((← whnf (← inferType e)).bindingDomain!) fun zs2 _ => do
117-
let k := (← mkNoConfusionCtorArg conName P).beta (xs ++ zs1 ++ zs2)
122+
let k := (← mkNoConfusionCtorArg conName P).beta (xs1 ++ zs1 ++ xs2 ++ zs2)
118123
let t ← mkArrow k P
119124
mkLambdaFVars zs2 t
120125
pure e
121126
mkLambdaFVars zs1 alt
122127
else
123128
let conName := info.ctors[i]!
124129
let alt := mkConst casesOnName (v.succ :: us)
125-
let alt := mkAppN alt (xs ++ #[motive] ++ ysx2)
130+
let alt := mkAppN alt (xs2 ++ #[motive2] ++ ysx2)
126131
let t2 ← inferType alt
127132
let altTypes2 ← arrowDomainsN info.numCtors t2
128133
let alts2 ← altTypes2.mapIdxM fun j altType2 => do
129134
forallTelescope altType2 fun zs2 _ => do
130135
if i = j then
131-
let k := (← mkNoConfusionCtorArg conName P).beta (xs ++ zs1 ++ zs2)
136+
let k := (← mkNoConfusionCtorArg conName P).beta (xs1 ++ zs1 ++ xs2 ++ zs2)
132137
let t ← mkArrow k P
133138
mkLambdaFVars zs2 t
134139
else
135140
mkLambdaFVars zs2 P
136141
let alt := mkAppN alt alts2
137142
mkLambdaFVars zs1 alt
138143
let e := mkAppN e alts
139-
mkLambdaFVars (xs ++ #[P] ++ ysx1 ++ ysx2) e
144+
mkLambdaFVars (#[P] ++ xs1 ++ ysx1 ++ xs2 ++ ysx2) e
140145

141146
addDecl (.defnDecl (← mkDefinitionValInferringUnsafe
142147
(name := declName)
@@ -220,19 +225,19 @@ def mkNoConfusionCoreImp (indName : Name) : MetaM Unit := do
220225
let casesOnInfo ← getConstVal casesOnName
221226
let v::us := casesOnInfo.levelParams.map mkLevelParam | panic! "unexpected universe levels on `casesOn`"
222227
trace[Meta.mkNoConfusion] m!"mkNoConfusionCoreImp for {declName}"
223-
let e ← forallBoundedTelescope (← inferType (mkConst noConfusionTypeName (v::us))) (some (info.numParams + 1)) fun xs t => do
224-
let params : Array Expr := xs[:info.numParams]
225-
let P := xs[info.numParams]!
226-
forallBoundedTelescope t (some (info.numIndices + 1)) fun ysx1 _ => do -- indices and major
227-
forallBoundedTelescope t (some (info.numIndices + 1)) fun ysx2 _ => do -- indices and major
228-
withPrimedNames ysx2 do
229-
withImplicitBinderInfos ((ysx1 ++ ysx2).push P) do
230-
let target1 := mkAppN (mkConst noConfusionTypeName (v :: us)) (params ++ #[P] ++ ysx1 ++ ysx1)
228+
let e ← forallBoundedTelescope (← inferType (mkConst noConfusionTypeName (v::us))) (some 1) fun xs t => do
229+
let P := xs[0]!
230+
forallBoundedTelescope t (some (info.numParams + info.numIndices + 1)) fun xs1 t => do -- params, indices and major
231+
forallBoundedTelescope t (some (info.numParams + info.numIndices + 1)) fun xs2 _ => do -- params, indices and major
232+
withImplicitBinderInfos ((xs1 ++ xs2).push P) do
233+
let params1 : Array Expr := xs1[:info.numParams]
234+
let ysx1 : Array Expr := xs1[info.numParams:]
235+
let target1 := mkAppN (mkConst noConfusionTypeName (v :: us)) (#[P] ++ xs1 ++ xs1)
231236
let motive1 ← mkLambdaFVars ysx1 target1
232237
let alts ← info.ctors.mapM fun ctor => do
233-
let ctorType ← inferType (mkAppN (mkConst ctor us) params)
234-
forallTelescopeReducing ctorType fun fs _ => do
235-
let kType := (← mkNoConfusionCtorArg ctor P).beta (params ++ fs ++ fs)
238+
let ctorType ← inferType (mkAppN (mkConst ctor us) params1)
239+
forallTelescopeReducing ctorType fun fs1 _ => do
240+
let kType := (← mkNoConfusionCtorArg ctor P).beta (params1 ++ fs1 ++ params1 ++ fs1)
236241
withLocalDeclD `k kType fun k => do
237242
let mut e := k
238243
let eqns ← arrowDomainsN kType.getNumHeadForalls kType
@@ -243,12 +248,12 @@ def mkNoConfusionCoreImp (indName : Name) : MetaM Unit := do
243248
e := mkApp e (← mkHEqRefl x)
244249
else
245250
throwError "unexpected equation {eqn} in `mkNoConfusionCtorArg` for {ctor}"
246-
mkLambdaFVars (fs ++ #[k]) e
247-
let e := mkAppN (mkConst casesOnName (v :: us)) (params ++ #[motive1] ++ ysx1 ++ alts)
248-
let target2 := mkAppN (mkConst noConfusionTypeName (v :: us)) (params ++ #[P] ++ ysx1 ++ ysx2)
249-
let motive2 ← mkLambdaFVars ysx2 target2
250-
let e ← mkEqNDRecTelescope motive2 e ysx1 ysx2
251-
mkLambdaFVars (params ++ #[P] ++ ysx1 ++ ysx2) e
251+
mkLambdaFVars (fs1 ++ #[k]) e
252+
let e := mkAppN (mkConst casesOnName (v :: us)) (params1 ++ #[motive1] ++ ysx1 ++ alts)
253+
let target2 := mkAppN (mkConst noConfusionTypeName (v :: us)) (#[P] ++ xs1 ++ xs2)
254+
let motive2 ← mkLambdaFVars xs2 target2
255+
let e ← mkEqNDRecTelescope motive2 e xs1 xs2
256+
mkLambdaFVars (#[P] ++ xs1 ++ xs2) e
252257

253258
addDecl (.defnDecl (← mkDefinitionValInferringUnsafe
254259
(name := declName)
@@ -265,8 +270,8 @@ def mkNoConfusionCoreImp (indName : Name) : MetaM Unit := do
265270

266271
/--
267272
Creates per-constructor no-confusion definitions. These specialize the general noConfusion
268-
declaration to equalities between two applications of the same constructor, to effectively cache
269-
the computation of `noConfusionType` for that constructor:
273+
declaration to (homogeneous!) equalities between two applications of the same constructor, to
274+
effectively cache the computation of `noConfusionType` for that constructor:
270275
271276
```
272277
def L.cons.noConfusion.{u_1, u} : {α : Type u} → {P : Sort u_1} →
@@ -282,6 +287,14 @@ def Vec.cons.noConfusion.{u_1, u} : {α : Type u} → {P : Sort u_1} →
282287
(n = n' → x = x' → xs ≍ xs' → P)
283288
→ P
284289
```
290+
291+
These are specialized to equal parameters. The main use of these theorems is `injection` through
292+
`Meta.mkNoConfusion`, which deals with homogeneous equalities, so no need for the generality.
293+
294+
These still accept a heterogeneous equality between the two constructor applications: if we tried
295+
to phrase this theroem with a homogeneous equality, this would force those constructor fields that
296+
appear in indices to be equal, which is too strong: we can have homogenenous equalities between
297+
two constructor applications with different fields but same indices.
285298
-/
286299
def mkNoConfusionCtors (declName : Name) : MetaM Unit := do
287300
-- Do not do anything unless can_elim_to_type.
@@ -312,11 +325,21 @@ def mkNoConfusionCtors (declName : Name) : MetaM Unit := do
312325
-- When the kernel checks this definition, it will perform the potentially expensive
313326
-- computation that `noConfusionType h` is equal to `$kType → P`
314327
let kType ← mkNoConfusionCtorArg ctor P
315-
let kType := kType.beta (xs ++ fields1 ++ fields2)
328+
let kType := kType.beta (xs ++ fields1 ++ xs ++ fields2)
329+
-- TODO: Turn HEq to Eq in kType
316330
withLocalDeclD `k kType fun k => do
317331
let mut e := mkConst noConfusionName (v :: us)
318-
e := mkAppN e (xs ++ #[P] ++ is1 ++ #[ctor1] ++ is2 ++ #[ctor2])
319-
-- eqs may have more Eq rather than HEq than expected by `noConfusion`
332+
e := mkAppN e (#[P] ++ xs ++ is1 ++ #[ctor1] ++ xs ++ is2 ++ #[ctor2])
333+
-- Pass rfl equalities for parameters
334+
for _ in [:xs.size] do
335+
let eq ← whnf (← whnfForall (← inferType e)).bindingDomain!
336+
if let some (_,i,_,_) := eq.heq? then
337+
e := mkApp e (← mkHEqRefl i)
338+
else if let some (_,i,_) := eq.eq? then
339+
e := mkApp e (← mkEqRefl i)
340+
else
341+
throwError "mkNoConfusion: unexpected equality `{eq}` as next argument to{inlineExpr (← inferType e)}"
342+
-- Equalities for indices. May require going from Eq to HEq
320343
for eq in eqs do
321344
let needsHEq := (← whnfForall (← inferType e)).bindingDomain!.isHEq
322345
if needsHEq && (← inferType eq).isEq then
@@ -336,11 +359,10 @@ def mkNoConfusionCtors (declName : Name) : MetaM Unit := do
336359
(hints := ReducibilityHints.abbrev)
337360
))
338361
setReducibleAttribute name
339-
let arity := ctorInfo.numParams + 1 + 2 * ctorInfo.numFields + indVal.numIndices + 1
362+
let arity := ctorInfo.numParams + 1 + 2 * ctorInfo.numFields + eqvs.size
340363
let fields := kType.getNumHeadForalls
341364
modifyEnv fun env => markNoConfusion env name (.perCtor arity fields)
342365

343-
344366
def mkNoConfusionCore (declName : Name) : MetaM Unit := do
345367
-- Do not do anything unless can_elim_to_type. TODO: Extract to util
346368
let .inductInfo indVal ← getConstInfo declName | return

src/Lean/Meta/Injective.lean

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -196,22 +196,23 @@ private structure MkHInjTypeResult where
196196
private def mkHInjType? (ctorVal : ConstructorVal) : MetaM (Option MkHInjTypeResult) := do
197197
let us := ctorVal.levelParams.map mkLevelParam
198198
let type ← elimOptParam ctorVal.type
199-
forallBoundedTelescope type ctorVal.numParams fun params type =>
200-
forallTelescope type fun args1 _ => do
201-
withImplicitBinderInfos (params ++ args1) do
199+
forallTelescope type fun args1 _ =>
200+
withImplicitBinderInfos args1 do
201+
let params1 := args1[:ctorVal.numParams]
202202
let k (args2 : Array Expr) : MetaM (Option MkHInjTypeResult) := do
203-
let lhs := mkAppN (mkAppN (mkConst ctorVal.name us) params) args1
204-
let rhs := mkAppN (mkAppN (mkConst ctorVal.name us) params) args2
203+
let params2 := args2[:ctorVal.numParams]
204+
let lhs := mkAppN (mkConst ctorVal.name us) args1
205+
let rhs := mkAppN (mkConst ctorVal.name us) args2
205206
let eq ← mkEqHEq lhs rhs
206207
let eqs ← mkEqs args1 args2
207208
if let some andEqs := mkAnd? eqs then
208209
let result ← mkArrow eq andEqs
209210
let some idxs1 ← getCtorAppIndices? lhs | return none
210211
let some idxs2 ← getCtorAppIndices? rhs | return none
211212
-- **Note**: We dot not skip here because the type of `noConfusion` does not.
212-
let idxEqs ← mkEqs idxs1 idxs2 (skipIfPropOrEq := false)
213+
let idxEqs ← mkEqs (params1 ++ idxs1) (params2 ++ idxs2) (skipIfPropOrEq := false)
213214
let result ← mkArrowN idxEqs result
214-
let thmType ← mkForallFVars params (← mkForallFVars args1 (← mkForallFVars args2 result))
215+
let thmType ← mkForallFVars args1 (← mkForallFVars args2 result)
215216
return some { thmType, us, numIndices := idxs1.size }
216217
else
217218
return none
@@ -231,10 +232,9 @@ private def failedToGenHInj (ctorVal : ConstructorVal) : MetaM α :=
231232
private partial def mkHInjectiveTheoremValue? (ctorVal : ConstructorVal) (typeInfo : MkHInjTypeResult) : MetaM (Option Expr) := do
232233
forallTelescopeReducing typeInfo.thmType fun xs type => do
233234
let noConfusionName := ctorVal.induct.str "noConfusion"
234-
let params := xs[*...ctorVal.numParams]
235-
let noConfusion := mkAppN (mkConst noConfusionName (0 :: typeInfo.us)) params
235+
let noConfusion := mkConst noConfusionName (0 :: typeInfo.us)
236236
let noConfusion := mkApp noConfusion type
237-
let n := xs.size - typeInfo.numIndices - 1
237+
let n := xs.size - ctorVal.numParams - typeInfo.numIndices - 1
238238
let eqs := xs[n...*].toArray
239239
let eqExprs ← eqs.mapM fun x => do
240240
match_expr (← inferType x) with

src/Lean/Meta/Tactic/Grind/Ctor.lean

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,28 +72,25 @@ private def propagateCtorHetero (a b : Expr) : GoalM Unit := do
7272
unless ctorVal₁.induct == ctorVal₂.induct do return ()
7373
let params₁ := args₁[*...ctorVal₁.numParams]
7474
let params₂ := args₂[*...ctorVal₂.numParams]
75-
let fields₁ := args₁[ctorVal₁.numParams...*]
76-
let fields₂ := args₂[ctorVal₂.numParams...*]
77-
if h : params₁.size ≠ params₂.size then return () else
78-
unless (← params₁.size.allM fun i h => isDefEq params₁[i] params₂[i]) do return ()
75+
if params₁.size ≠ params₂.size then return () else
7976
unless us₁.length == us₂.length do return ()
8077
unless (← us₁.zip us₂ |>.allM fun (u₁, u₂) => isLevelDefEq u₁ u₂) do return ()
8178
let gen := max (← getGeneration a) (← getGeneration b)
8279
if ctorName₁ == ctorName₂ then
8380
let hinjDeclName := mkHInjectiveTheoremNameFor ctorName₁
8481
unless (← getEnv).containsOnBranch hinjDeclName do
8582
let _ ← executeReservedNameAction hinjDeclName
86-
let proof := mkAppN (mkConst hinjDeclName us₁) params
87-
let proof := mkAppN (mkAppN proof fields₁) fields
83+
let proof := mkConst hinjDeclName us₁
84+
let proof := mkAppN (mkAppN proof args₁) args
8885
addNewRawFact proof (← inferType proof) gen (.inj (.decl hinjDeclName))
8986
else
9087
let some indices₁ ← getCtorAppIndices? a | return ()
9188
let some indices₂ ← getCtorAppIndices? b | return ()
9289
let noConfusionName := ctorVal₁.induct.str "noConfusion"
93-
let noConfusion := mkAppN (mkConst noConfusionName (0 :: us₁)) params₁
90+
let noConfusion := mkConst noConfusionName (0 :: us₁)
9491
let noConfusion := mkApp noConfusion (← getFalseExpr)
95-
let noConfusion := mkApp (mkAppN noConfusion indices₁) a
96-
let noConfusion := mkApp (mkAppN noConfusion indices₂) b
92+
let noConfusion := mkApp (mkAppN noConfusion (params₁ ++ indices₁)) a
93+
let noConfusion := mkApp (mkAppN noConfusion (params₂ ++ indices₂)) b
9794
let proof := noConfusion
9895
addNewRawFact proof (← inferType proof) gen (.inj (.decl noConfusionName))
9996

tests/lean/run/def15.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ def head {α} : (as : List α) → as ≠ [] → α
44
| [], h => absurd rfl h
55
| a::as, _ => a
66

7-
theorem head_cons {α} (a : α) (as : List α) : head (a::as) (fun h => List.noConfusion h) = a :=
7+
theorem head_cons {α} (a : α) (as : List α) : head (a::as) (fun h => List.noConfusion rfl (heq_of_eq h)) = a :=
88
rfl
99

1010
theorem head_cons' {α} (a : α) (as : List α) (h : a::as ≠ []) : head (a::as) h = a :=

tests/lean/run/issue11560.lean

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
structure Fin' (n : Nat) where
2+
mk ::
3+
val : Nat
4+
isLt : LT.lt val n
5+
6+
example
7+
(a b c d : Nat)
8+
(h1 : c.succ < a)
9+
(h2 : d.succ < b)
10+
(hab : a = b)
11+
(hcd : @Fin'.mk a c.succ h1 ≍ @Fin'.mk b d.succ h2) :
12+
c = d := Fin'.mk.noConfusion hab hcd (fun h => Nat.succ.noConfusion h fun h' => h')
13+
14+
example
15+
(a b c d : Nat)
16+
(h1 : c.succ < a)
17+
(h2 : d.succ < b)
18+
(hab : a = b)
19+
(hcd : @Fin'.mk a c.succ h1 ≍ @Fin'.mk b d.succ h2) :
20+
c = d := by
21+
grind

tests/lean/run/noConfusionCtorInjection.lean

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ inductive L (α : Type u) : Type u where
66
/--
77
info: theorem L.cons.inj.{u} : ∀ {α : Type u} {x : α} {xs : L α} {x_1 : α} {xs_1 : L α},
88
L.cons x xs = L.cons x_1 xs_1 → x = x_1 ∧ xs = xs_1 :=
9-
fun {α} {x} {xs} {x_1} {xs_1} x_2 => L.cons.noConfusion x_2 fun x_eq xs_eq => ⟨x_eq, xs_eq⟩
9+
fun {α} {x} {xs} {x_1} {xs_1} x_2 =>
10+
L.cons.noConfusion (Eq.refl α) (heq_of_eq x_2) fun x_eq xs_eq => ⟨eq_of_heq x_eq, eq_of_heq xs_eq⟩
1011
-/
1112
#guard_msgs in
1213
#print L.cons.inj
@@ -20,7 +21,8 @@ theorem ex1 (h : L.cons x xs = L.cons y ys) : x = y ∧ xs = ys := by
2021
/--
2122
info: theorem ex1.{u_1} : ∀ {α : Type u_1} {x : α} {xs : L α} {y : α} {ys : L α},
2223
L.cons x xs = L.cons y ys → x = y ∧ xs = ys :=
23-
fun {α} {x} {xs} {y} {ys} h => L.cons.noConfusion h fun x_eq xs_eq => ⟨x_eq, xs_eq⟩
24+
fun {α} {x} {xs} {y} {ys} h =>
25+
L.cons.noConfusion (Eq.refl α) (heq_of_eq h) fun x_eq xs_eq => ⟨eq_of_heq x_eq, eq_of_heq xs_eq⟩
2426
-/
2527
#guard_msgs in #print ex1
2628

0 commit comments

Comments
 (0)