From d301b1f5458bd3481c958a56e0dd7ce3473534d1 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Tue, 6 Jan 2026 19:35:01 +0100 Subject: [PATCH 001/141] feat: A linter for instances that overlap on data --- Mathlib.lean | 1 + Mathlib/Tactic.lean | 1 + .../Tactic/Linter/OverlappingInstances.lean | 68 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 Mathlib/Tactic/Linter/OverlappingInstances.lean diff --git a/Mathlib.lean b/Mathlib.lean index 7baa0c611101a7..ca431dd5f7a1ee 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -6648,6 +6648,7 @@ public import Mathlib.Tactic.Linter.Lint public import Mathlib.Tactic.Linter.MinImports public import Mathlib.Tactic.Linter.Multigoal public import Mathlib.Tactic.Linter.OldObtain +public import Mathlib.Tactic.Linter.OverlappingInstances public import Mathlib.Tactic.Linter.PPRoundtrip public import Mathlib.Tactic.Linter.PrivateModule public import Mathlib.Tactic.Linter.Style diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index 9ee549699eaab3..82ad54d613876d 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -169,6 +169,7 @@ public import Mathlib.Tactic.Linter.Lint public import Mathlib.Tactic.Linter.MinImports public import Mathlib.Tactic.Linter.Multigoal public import Mathlib.Tactic.Linter.OldObtain +public import Mathlib.Tactic.Linter.OverlappingInstances public import Mathlib.Tactic.Linter.PPRoundtrip public import Mathlib.Tactic.Linter.PrivateModule public import Mathlib.Tactic.Linter.Style diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean new file mode 100644 index 00000000000000..002b403e7fcd19 --- /dev/null +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -0,0 +1,68 @@ +/- +Copyright (c) 2026 Jovan Gerbscheid. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jovan Gerbscheid +-/ +module + +public import Mathlib.Init + +/- +# A linter to declarations with local instances that have overlapping data + +We want to avoid this because this lead to instance diamonds +-/ + +open Lean Meta Batteries Tactic Lint + +public meta section + +/-- Given an instance `e`, conpute return all data carrying classes that are +the type of `e` itself, or a child class. -/ +private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := #[]) : + StateRefT NameSet MetaM (Array Expr) := do + let eType ← whnf (← inferType e) + if ← isProp eType then return acc + let .const structName us := eType.getForallBody.getAppFn + | throwError "{e} is not an instance of a structure" + if (← get).contains structName then return acc + modify (·.insert structName) + let some info := getStructureInfo? (← getEnv) structName | return acc + info.parentInfo.foldlM (init := acc.push eType) fun acc info ↦ do + if ← isInstance info.projFn then + let proj := Expr.app (mkAppN (.const info.projFn us) eType.getAppArgs) e + getStructureDataProjections proj acc + else + return acc + +/-- Run the overlapping instances linter on `declName`. -/ +def findDuplicateDataInstances (declName : Name) : CoreM (Option MessageData) := MetaM.run' do + Core.checkSystem "overlappingInstances" + forallTelescope (← getConstInfo declName).type fun _ _ ↦ do + let mut result := none + let mut insts : Std.HashMap Expr Expr := {} + for { fvar, .. } in ← getLocalInstances do + unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue + let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do + (← getStructureDataProjections (mkAppN fvar xs) |>.run' {}).mapM (mkForallFVars xs) + + for cls in projClasses do + if let some fvar' := insts[cls]? then + let type ← inferType fvar; let type' ← inferType fvar' + if type == type' then + result := m!"{result.getD m!""}There are multiple different instances of `{type}`\n\n" + break + else + result := m!"{result.getD m!""}`{type}` and `{type'}` both imply `{cls}`\n\n" + else + insts := insts.insert cls fvar + result.mapM addMessageContextFull + +/-- A linter for declarations which have instance hypotheses that have overlapping data. -/ +@[env_linter] def overlappingInstances : Lint.Linter where + noErrorsFound := "No declarations have overlapping instance arguments" + errorsFound := "Some declarations have overlapping instance arguments" + test := fun declName => do + if declName.isInternal then return none + if congrKindsExt.contains (← getEnv) declName then return none + findDuplicateDataInstances declName From cc6b2b140cdf705c035a5b375311aa671184efb7 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 6 Jan 2026 23:23:09 -0500 Subject: [PATCH 002/141] chore: Init and import management --- Mathlib/Init.lean | 1 + Mathlib/Tactic/Linter/DirectoryDependency.lean | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Mathlib/Init.lean b/Mathlib/Init.lean index 154cf0e3a5312c..cac625fb44baf4 100644 --- a/Mathlib/Init.lean +++ b/Mathlib/Init.lean @@ -16,6 +16,7 @@ public import Mathlib.Tactic.Linter.FlexibleLinter public import Mathlib.Tactic.Linter.Lint public import Mathlib.Tactic.Linter.Multigoal public import Mathlib.Tactic.Linter.OldObtain +public import Mathlib.Tactic.Linter.OverlappingInstances public import Mathlib.Tactic.Linter.PrivateModule public import Mathlib.Tactic.Linter.TacticDocumentation -- The following import contains the environment extension for the unused tactic linter. diff --git a/Mathlib/Tactic/Linter/DirectoryDependency.lean b/Mathlib/Tactic/Linter/DirectoryDependency.lean index 13bd8599abfe1a..28e66d6e914232 100644 --- a/Mathlib/Tactic/Linter/DirectoryDependency.lean +++ b/Mathlib/Tactic/Linter/DirectoryDependency.lean @@ -241,6 +241,10 @@ def allowedImportDirs : NamePrefixRel := .ofArray #[ -- For more fine-grained exceptions of the next two imports, one needs to rename that file. (`Mathlib.Tactic.Linter, `ImportGraph), (`Mathlib.Tactic.Linter, `Mathlib.Tactic.MinImports), + (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.ContextInfo), + (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Elab.Tactic.Meta), + (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Environment), + (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Elab.InfoTree), (`Mathlib.Tactic.Linter.TextBased, `Mathlib.Data.Nat.Notation), (`Mathlib.Tactic.Linter.UnusedInstancesInType, `Mathlib.Lean.Expr.Basic), (`Mathlib.Tactic.Linter.UnusedInstancesInType, `Mathlib.Lean.Environment), From b4a8dbb49e1d4dff8f46e77343c5c140013072e8 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 6 Jan 2026 23:23:54 -0500 Subject: [PATCH 003/141] feat: `localInstances` argument to `ContextInfo.runMetaMWithMessages` --- Mathlib/Lean/ContextInfo.lean | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/Mathlib/Lean/ContextInfo.lean b/Mathlib/Lean/ContextInfo.lean index 4d86fd8db12d97..26356a3d6e4486 100644 --- a/Mathlib/Lean/ContextInfo.lean +++ b/Mathlib/Lean/ContextInfo.lean @@ -58,15 +58,23 @@ def runCoreMWithMessages (info : ContextInfo) (x : CoreM α) : CommandElabM α : Copy of `ContextInfo.runMetaM` that makes use of the `CommandElabM` context for: * message logging (messages produced by the `CoreM` action are migrated back), * metavariable generation, -* auxiliary declaration generation, -* local instances. +* auxiliary declaration generation + +If `localInstances := none` (the default), `runMetaMWithMessages` will refresh the local instances +in the context. -/ -def runMetaMWithMessages (info : ContextInfo) (lctx : LocalContext) (x : MetaM α) : CommandElabM α := do +def runMetaMWithMessages (info : ContextInfo) (lctx : LocalContext) (x : MetaM α) + (localInstances : Option LocalInstances := none) : CommandElabM α := do (·.1) <$> info.runCoreMWithMessages (Lean.Meta.MetaM.run - (ctx := { lctx := lctx }) (s := { mctx := info.mctx }) <| - -- Update the local instances, otherwise typeclass search would fail to see anything in the - -- local context. - Meta.withLocalInstances (lctx.decls.toList.filterMap id) <| x) + -- Use provided local instances here to avoid a closure. + (ctx := { lctx := lctx, localInstances := localInstances.getD #[] }) + (s := { mctx := info.mctx }) <| + if localInstances.isSome then + x + else + -- Update the local instances, otherwise typeclass search would fail to see anything in the + -- local context. + Meta.withLocalInstances (lctx.decls.toList.filterMap id) <| x) /-- Run a tactic computation in the context of an infotree node. -/ def runTactic (ctx : ContextInfo) (i : TacticInfo) (goal : MVarId) (x : MVarId → MetaM α) : From 81769673ad2b2a0f692a8290155d3666faae0ac5 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 6 Jan 2026 23:24:38 -0500 Subject: [PATCH 004/141] feat: `getDeclBodyInfos` --- Mathlib/Lean/Elab/InfoTree.lean | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index 5c75fdda62ed9c..6786dfcbc42d30 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -119,6 +119,24 @@ def getDeclsByBody (t : InfoTree) : List Name := else decls | _ => decls +/-- Gets the first child info of each `Lean.Elab.BodyInfo`, which should be the only child, and +should be a `TermInfo`, `PartialTermInfo`, or `TacticInfo`. `getDeclBodyInfos` does not validate +either of these conditions. -/ +def getDeclBodyInfos (t : InfoTree) : List (ContextInfo × Info) := + t.foldInfoTree (init := []) fun ctx t acc => + match t with + | .node (.ofCustomInfo i) body => + if i.value.typeName == ``Lean.Elab.Term.BodyInfo then + if h : 0 < body.size then + -- See through `.context`s instead of just matching on `.node`: + let result? := body[0].onHighestNode? (ctx? := ctx) fun ctx i _ => (ctx, i) + if let some result := result? then + result :: acc + else acc + else acc + else acc + | _ => acc + /-- Get the declarations elaborated in the infotree `t` which are theorems according to the environment. This includes e.g. `instance`s of `Prop` classes in addition to declarations declared From 8fa638f8b3c74cd8ff99d58a4604a6204623cf39 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 6 Jan 2026 23:25:01 -0500 Subject: [PATCH 005/141] chore: import changes + header mgmt --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 002b403e7fcd19..c5204ff3f56b77 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -1,22 +1,26 @@ /- Copyright (c) 2026 Jovan Gerbscheid. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Jovan Gerbscheid +Authors: Jovan Gerbscheid, Thomas R. Murrills -/ module -public import Mathlib.Init +public meta import Mathlib.Lean.Elab.InfoTree +public meta import Lean.Elab.Command +public meta import Mathlib.Lean.ContextInfo -/- +/-! # A linter to declarations with local instances that have overlapping data We want to avoid this because this lead to instance diamonds -/ -open Lean Meta Batteries Tactic Lint +open Lean Meta Elab public meta section +namespace Mathlib.Tactic.OverlappingInstances + /-- Given an instance `e`, conpute return all data carrying classes that are the type of `e` itself, or a child class. -/ private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := #[]) : From a2594a12dd26bc242f2488a8cf9a23340ad8c356 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 6 Jan 2026 23:26:36 -0500 Subject: [PATCH 006/141] refactor: return data from `findOverlappingDataInstances`, remove env linter --- .../Tactic/Linter/OverlappingInstances.lean | 58 ++++++++++--------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index c5204ff3f56b77..6132371c89ccbf 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -39,34 +39,36 @@ private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := else return acc -/-- Run the overlapping instances linter on `declName`. -/ -def findDuplicateDataInstances (declName : Name) : CoreM (Option MessageData) := MetaM.run' do - Core.checkSystem "overlappingInstances" - forallTelescope (← getConstInfo declName).type fun _ _ ↦ do - let mut result := none - let mut insts : Std.HashMap Expr Expr := {} - for { fvar, .. } in ← getLocalInstances do - unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue - let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do - (← getStructureDataProjections (mkAppN fvar xs) |>.run' {}).mapM (mkForallFVars xs) +/-- An overlap between two local instances. This is introduced for readability, as all fields are +`Expr`s. -/ +structure Overlap where + /-- A local instance free variable whose data-carrying projections overlap with `fvar₂`. -/ + fvar₁ : Expr × Bool + /-- A local instance free variable whose data-carrying projections overlap with `fvar₁`. -/ + fvar₂ : Expr × Bool + /-- A type class on which `fvar₁` and `fvar₂`'s data-carrying projections overlap. -/ + overlap : Expr - for cls in projClasses do - if let some fvar' := insts[cls]? then - let type ← inferType fvar; let type' ← inferType fvar' - if type == type' then - result := m!"{result.getD m!""}There are multiple different instances of `{type}`\n\n" - break - else - result := m!"{result.getD m!""}`{type}` and `{type'}` both imply `{cls}`\n\n" +/-- Find data-carrying overlaps between the current local instances of the `MetaM` context. -/ +def findOverlappingDataInstances : MetaM (Array Overlap) := do + let mut overlaps : Array Overlap := #[] + /- The `Bool` indicates whether the given class key is exactly the type of the associated `fvar` + value. This is used for error reporting. -/ + let mut insts : Std.HashMap Expr (Expr × Bool) := {} + for { fvar := fvar₁, .. } in ← getLocalInstances do + unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue + let projClasses ← forallTelescope (← inferType fvar₁) fun xs _ ↦ do + (← getStructureDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM (mkForallFVars xs) + for h : clsIdx in 0...projClasses.size do + let cls := projClasses[clsIdx] + if let some (fvar₂, isTypeOfFVar₂) := insts[cls]? then + overlaps := overlaps.push { + fvar₁ := (fvar₁, clsIdx = 0) + fvar₂ := (fvar₂, isTypeOfFVar₂) + overlap := cls } + if clsIdx = 0 && isTypeOfFVar₂ then + break -- Don't consider further projections of this local instance else - insts := insts.insert cls fvar - result.mapM addMessageContextFull + insts := insts.insert cls (fvar₁, clsIdx = 0) + return overlaps -/-- A linter for declarations which have instance hypotheses that have overlapping data. -/ -@[env_linter] def overlappingInstances : Lint.Linter where - noErrorsFound := "No declarations have overlapping instance arguments" - errorsFound := "Some declarations have overlapping instance arguments" - test := fun declName => do - if declName.isInternal then return none - if congrKindsExt.contains (← getEnv) declName then return none - findDuplicateDataInstances declName From 72cd2cbc5fb44019224f026d3a1f15e03d9b7fe1 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 00:09:57 -0500 Subject: [PATCH 007/141] feat: syntax linter `overlappingInstances` --- .../Tactic/Linter/OverlappingInstances.lean | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 6132371c89ccbf..656bd570b198cc 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -72,3 +72,129 @@ def findOverlappingDataInstances : MetaM (Array Overlap) := do insts := insts.insert cls (fvar₁, clsIdx = 0) return overlaps +/-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ +register_option linter.overlappingInstances : Bool := { + defValue := false + descr := "enable the overlapping instances linter. This only lints against data-carrying \ + overlaps and on declaration bodies." +} + +/-- Surrounds an expression representing the type of an instance with square brackets, taking care +to group and nest appropriately. -/ +private def _root_.Lean.MessageData.ofInstanceType (e : Expr) : MessageData := + m!"{e}".sbracket + +open Linter in +/-- +Lints against data-carrying overlaps between instances in the local contexts of declarations. + +Note: currently does not respect `set_option`. +-/ +def overlappingInstances : Linter where + run cmd := do + unless getLinterValue linter.overlappingInstances (← getLinterOptions) do + return + /- TODO: use `withSetOptionIn` when either it's fixed via the open lean PR or + `unusedFintypeInType` lands with a workaround -/ + -- Note: we don't break on errors; we want to lint even on partial declarations + for t in ← getInfoTrees do + for (ctx, info) in t.getDeclBodyInfos do + let (lctx, localInstances, remainingType?) ← do + match info with + | .ofTacticInfo i => do + let g :: _ := i.goalsBefore | continue + let some decl := i.mctxBefore.findDecl? g | continue + pure (decl.lctx, some decl.localInstances, some decl.type) + | .ofTermInfo i + | .ofPartialTermInfo i => pure (i.lctx, none, i.expectedType?) + | _ => continue -- Unreachable. TODO: refactor? + let namingCtx : NamingContext := { + currNamespace := ← getCurrNamespace + openDecls := ← getOpenDecls + } + let outerEnv ← getEnv + ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| + withRef (← getRef) do + letI forallTelescope? expectedType? (k : Array Expr → Option Expr → MetaM Unit) := + if let some type := expectedType? then + forallTelescope type fun fvars ty => k fvars ty + else + k #[] expectedType? + forallTelescope? remainingType? fun newFVars _ => do + let overlaps ← findOverlappingDataInstances + unless overlaps.isEmpty do + let mut collectedByOverlap : Std.HashMap Expr (Std.HashSet (Expr × Bool)) := {} + for { fvar₁, fvar₂, overlap } in overlaps do + collectedByOverlap := collectedByOverlap.alter overlap fun set? => + (set?.getD ∅).insert fvar₁ |>.insert fvar₂ + + /- This is only updated (and then, only in the `lctx` field) in the case that we need + to erase the aux fvar from the lctx for pretty printing. -/ + let mut ppCtx : MessageDataContext := { + env := ← getEnv + opts := ← getOptions + mctx := ← getMCtx + lctx := ← getLCtx } + + let declMsg ← + -- TODO: why does lean need help with the types around here? + -- `MessageData.`, `: FVarId` + if let some decl := ctx.parentDecl? then + let auxFVar? := + (← getLCtx).auxDeclToFullName.toList.find? fun ((fvarId : FVarId), name) => + name == decl + -- See if the contant's type is available in the outer environment. + if outerEnv.findConstVal? decl (skipRealize := true) |>.isSome then + if let some (fvarId, _) := auxFVar? then + /- We have to prepare the message context carefully, because currently the + name `foo` may clash with an aux decl of the same name in the local context. + This leads to error messages printed as e.g. `_root_.foo` or + `.foo`. To avoid this, we erase the aux decl from the local + context; since the type shouldn't depend on this, we hope that nothing goes + wrong as a result. -/ + ppCtx := { ppCtx with lctx := ppCtx.lctx.erase fvarId } + pure m!"declaration `{.ofConstName decl}`" + else + /- See if the local context has an aux decl with this full name, in case the declaration is incomplete; this lets us show e.g. metavariables in the type. -/ + let auxFVar? := + (← getLCtx).auxDeclToFullName.toList.find? fun ((fvarId : FVarId), name) => + name == decl + if let some (fvarId, _) := auxFVar? then + pure m!"declaration `{mkFVar fvarId}`" + else + /- Otherwise, just don't bother with a hover, but try our best to print it + nicely. We could probably do better by accounting for namespaces, but it + looks like the API (`unresolveNameGlobal`) is there only for declarations + which actually exist. -/ + pure m!"declaration `{privateToUserName decl}`" + else + pure m!"current declaration" + + let mut msg := m!"The {declMsg} has instance hypotheses that overlap on \ + data-carrying components." + + for (overlap, fvars) in collectedByOverlap do + let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => + if isDirect then .inl fvar else .inr fvar + let overlapType := m!"`{.ofInstanceType overlap}`" + let indirectTypes := MessageData.andList <|← indirect.mapM fun fvar => + return m!"`{.ofInstanceType <|← inferType fvar}`" + msg := msg ++ "\n\n" + msg := msg ++ + if indirect.isEmpty then + -- Necessarily plural: + m!"There are {direct.length} instances of {overlapType}." + else + if direct.isEmpty then + m!"{overlapType} is provided by {indirectTypes}." + else if let [direct] := direct then + m!"There is an instance of {overlapType} in the local context, but it is \ + also provided by {indirectTypes}." + else + m!"There are {direct.length} instances of {overlapType} in the local \ + context, and it is also provided by {indirectTypes}." + -- TODO: better logging location + -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant + logWarning <| msg.withContext ppCtx + +initialize addLinter overlappingInstances From e4cc55efb7463576446dc419ad4b8d3f6efff8ea Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 00:10:20 -0500 Subject: [PATCH 008/141] test: overlapping instances (first pass; all pass, but not comprehensive) --- MathlibTest/OverlappingInstances.lean | 87 +++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 MathlibTest/OverlappingInstances.lean diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean new file mode 100644 index 00000000000000..4cdbe5d9cd4804 --- /dev/null +++ b/MathlibTest/OverlappingInstances.lean @@ -0,0 +1,87 @@ +module + +import Mathlib.Tactic.Linter.OverlappingInstances + +set_option linter.overlappingInstances true + +namespace Lean + +class Bar (α : Type) where + a : α + +class Baz (β : Type) where + b : β + +class Baq (β : Type) where + b : β + +class Foo (α) (β) extends Bar α, Baz β + + +class Foo' (α) (β) extends Bar α, Baq β + +/-- +error: unsolved goals +inst✝¹ inst✝ : Add Nat +⊢ [Add Nat] → [Add Nat] → Bool +--- +warning: The declaration `foo` has instance hypotheses that overlap on data-carrying components. + +There are 4 instances of `[Add Nat]`. +-/ +#guard_msgs in +def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by + skip + + +/-- +warning: The declaration `foo'` has instance hypotheses that overlap on data-carrying components. + +`[Bar Nat]` is provided by `[Foo' Nat String]` and `[Foo Nat Bool]`. +-/ +#guard_msgs in +def foo' [Foo Nat Bool] [Foo' Nat String] : Bool := by + exact true + + +/-- +warning: The declaration `foo''` has instance hypotheses that overlap on data-carrying components. + +`[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. + +There are 2 instances of `[Foo Nat Bool]`. +-/ +#guard_msgs in +def foo'' [Foo Nat Bool] [Foo Nat Bool] [Foo' Nat String] : Bool := true + +/-- +warning: The declaration `foo'''` has instance hypotheses that overlap on data-carrying components. + +There are 2 instances of `[Foo Nat Bool]`. +-/ +#guard_msgs in +def foo''' [Foo Nat Bool] [Foo Nat Bool] : Bool := true + +/-- +error: Failed to infer type of definition `foo''''` +--- +warning: The declaration `foo''''` has instance hypotheses that overlap on data-carrying components. + +There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[Foo Nat Bool]`. + +There are 2 instances of `[Foo Nat Bool]`. +-/ +#guard_msgs in +def foo'''' [Foo Nat Bool] [Foo Nat Bool] [Bar Nat] := sorry + +-- Correct? Might not have `Bar Nat` if we can't provide `α`, but might if we can. +-- Only needs `(usedOnly := true)` in `mkForallFVars` to change behavior. +/-- +error: unsolved goals +inst✝¹ : Foo Nat Bool +inst✝ : (α : Type) → Foo' Nat α +⊢ Bool +-/ +#guard_msgs in +def fooForall [Foo Nat Bool] [∀ α, Foo' Nat α] : Bool := by + skip From 3167b84840d5413954a280acdbb913eee5043935 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:32:13 -0500 Subject: [PATCH 009/141] chore: remove prettty printing logic for now --- .../Tactic/Linter/OverlappingInstances.lean | 52 +++---------------- 1 file changed, 8 insertions(+), 44 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 656bd570b198cc..d651366dc1f27b 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -128,50 +128,14 @@ def overlappingInstances : Linter where collectedByOverlap := collectedByOverlap.alter overlap fun set? => (set?.getD ∅).insert fvar₁ |>.insert fvar₂ - /- This is only updated (and then, only in the `lctx` field) in the case that we need - to erase the aux fvar from the lctx for pretty printing. -/ - let mut ppCtx : MessageDataContext := { - env := ← getEnv - opts := ← getOptions - mctx := ← getMCtx - lctx := ← getLCtx } - - let declMsg ← - -- TODO: why does lean need help with the types around here? - -- `MessageData.`, `: FVarId` - if let some decl := ctx.parentDecl? then - let auxFVar? := - (← getLCtx).auxDeclToFullName.toList.find? fun ((fvarId : FVarId), name) => - name == decl - -- See if the contant's type is available in the outer environment. - if outerEnv.findConstVal? decl (skipRealize := true) |>.isSome then - if let some (fvarId, _) := auxFVar? then - /- We have to prepare the message context carefully, because currently the - name `foo` may clash with an aux decl of the same name in the local context. - This leads to error messages printed as e.g. `_root_.foo` or - `.foo`. To avoid this, we erase the aux decl from the local - context; since the type shouldn't depend on this, we hope that nothing goes - wrong as a result. -/ - ppCtx := { ppCtx with lctx := ppCtx.lctx.erase fvarId } - pure m!"declaration `{.ofConstName decl}`" - else - /- See if the local context has an aux decl with this full name, in case the declaration is incomplete; this lets us show e.g. metavariables in the type. -/ - let auxFVar? := - (← getLCtx).auxDeclToFullName.toList.find? fun ((fvarId : FVarId), name) => - name == decl - if let some (fvarId, _) := auxFVar? then - pure m!"declaration `{mkFVar fvarId}`" - else - /- Otherwise, just don't bother with a hover, but try our best to print it - nicely. We could probably do better by accounting for namespaces, but it - looks like the API (`unresolveNameGlobal`) is there only for declarations - which actually exist. -/ - pure m!"declaration `{privateToUserName decl}`" + -- For now, no hovers, since the name clashes with the aux decl of the same name in + -- the lctx. TODO: account for this. + let mut msg := m!"The \ + {if let some decl := ctx.parentDecl? then + m!"declaration `{privateToUserName decl}`" else - pure m!"current declaration" - - let mut msg := m!"The {declMsg} has instance hypotheses that overlap on \ - data-carrying components." + "current declaration"} \ + has instance hypotheses which overlap on data-carrying components." for (overlap, fvars) in collectedByOverlap do let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => @@ -195,6 +159,6 @@ def overlappingInstances : Linter where context, and it is also provided by {indirectTypes}." -- TODO: better logging location -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant - logWarning <| msg.withContext ppCtx + logWarning <|← addMessageContextFull msg initialize addLinter overlappingInstances From 664a00d8bfbdf5ab7ae683723c639ca77855157f Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:32:34 -0500 Subject: [PATCH 010/141] chore: simplify and document some internals --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index d651366dc1f27b..bb2ed01b1ebfa6 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -115,14 +115,21 @@ def overlappingInstances : Linter where let outerEnv ← getEnv ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| withRef (← getRef) do - letI forallTelescope? expectedType? (k : Array Expr → Option Expr → MetaM Unit) := + /- If there's a remaining expected type, then telescope into it in case it contains more + instance hypotheses. For now, we don't use the new fvars or remaining type for anything, + but these could be passed to `k`. -/ + letI forallTelescope? expectedType? (k : MetaM Unit) := if let some type := expectedType? then - forallTelescope type fun fvars ty => k fvars ty + forallTelescope type fun _ _ => k else - k #[] expectedType? - forallTelescope? remainingType? fun newFVars _ => do + k + forallTelescope? remainingType? do let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do + /- The fvars overlapping on a given class. For each class belonging to some overlap, + assign to it the fvars which have it as a projection, preserving the data as to + whether these fvars were instances of the class itself (as opposed to projecting to + it) in the `Bool`. -/ let mut collectedByOverlap : Std.HashMap Expr (Std.HashSet (Expr × Bool)) := {} for { fvar₁, fvar₂, overlap } in overlaps do collectedByOverlap := collectedByOverlap.alter overlap fun set? => From d446fbda14996defcd8b782533b8e21338120d63 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:33:40 -0500 Subject: [PATCH 011/141] docs: misc. --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index bb2ed01b1ebfa6..47639cc718a353 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -42,9 +42,13 @@ private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := /-- An overlap between two local instances. This is introduced for readability, as all fields are `Expr`s. -/ structure Overlap where - /-- A local instance free variable whose data-carrying projections overlap with `fvar₂`. -/ + /-- A local instance free variable whose data-carrying projections overlap with `fvar₂`. The + `Bool` indicates whether `fvar₁ == overlap`; this guides the logic and is used for error + reporting. -/ fvar₁ : Expr × Bool - /-- A local instance free variable whose data-carrying projections overlap with `fvar₁`. -/ + /-- A local instance free variable whose data-carrying projections overlap with `fvar₁`. The + `Bool` indicates whether `fvar₂ == overlap`; this guides the logic and is used for error + reporting. -/ fvar₂ : Expr × Bool /-- A type class on which `fvar₁` and `fvar₂`'s data-carrying projections overlap. -/ overlap : Expr From 73b6a3de9b24530c7b182bd5ec842a9cb0a4a4b4 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:43:07 -0500 Subject: [PATCH 012/141] test: update tests --- MathlibTest/OverlappingInstances.lean | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 4cdbe5d9cd4804..a4a27dfcb461ff 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -25,7 +25,7 @@ error: unsolved goals inst✝¹ inst✝ : Add Nat ⊢ [Add Nat] → [Add Nat] → Bool --- -warning: The declaration `foo` has instance hypotheses that overlap on data-carrying components. +warning: The declaration `Lean.foo` has instance hypotheses which overlap on data-carrying components. There are 4 instances of `[Add Nat]`. -/ @@ -35,7 +35,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- -warning: The declaration `foo'` has instance hypotheses that overlap on data-carrying components. +warning: The declaration `Lean.foo'` has instance hypotheses which overlap on data-carrying components. `[Bar Nat]` is provided by `[Foo' Nat String]` and `[Foo Nat Bool]`. -/ @@ -45,7 +45,7 @@ def foo' [Foo Nat Bool] [Foo' Nat String] : Bool := by /-- -warning: The declaration `foo''` has instance hypotheses that overlap on data-carrying components. +warning: The declaration `Lean.foo''` has instance hypotheses which overlap on data-carrying components. `[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. @@ -55,7 +55,7 @@ There are 2 instances of `[Foo Nat Bool]`. def foo'' [Foo Nat Bool] [Foo Nat Bool] [Foo' Nat String] : Bool := true /-- -warning: The declaration `foo'''` has instance hypotheses that overlap on data-carrying components. +warning: The declaration `Lean.foo'''` has instance hypotheses which overlap on data-carrying components. There are 2 instances of `[Foo Nat Bool]`. -/ @@ -65,7 +65,7 @@ def foo''' [Foo Nat Bool] [Foo Nat Bool] : Bool := true /-- error: Failed to infer type of definition `foo''''` --- -warning: The declaration `foo''''` has instance hypotheses that overlap on data-carrying components. +warning: The declaration `Lean.foo''''` has instance hypotheses which overlap on data-carrying components. There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[Foo Nat Bool]`. From 511c9420db5a20173c219cd5d1d3978c2c7c181d Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:44:01 -0500 Subject: [PATCH 013/141] tweak: don't break, just record "dominating" cases --- .../Tactic/Linter/OverlappingInstances.lean | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 47639cc718a353..f75a2dc83e9df3 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -63,15 +63,22 @@ def findOverlappingDataInstances : MetaM (Array Overlap) := do unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue let projClasses ← forallTelescope (← inferType fvar₁) fun xs _ ↦ do (← getStructureDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM (mkForallFVars xs) + /- The fvars that are either projections of the current fvar or have projections equal to the + current fvar. In both cases we want to ignore further matches against these fvars. + + For less verbose error reporting, we would ideally also ignore overlaps which share a parent; + we may eventually want a different data structure for `projClasses` for this. -/ + let mut done : Array Expr := #[] for h : clsIdx in 0...projClasses.size do let cls := projClasses[clsIdx] if let some (fvar₂, isTypeOfFVar₂) := insts[cls]? then - overlaps := overlaps.push { - fvar₁ := (fvar₁, clsIdx = 0) - fvar₂ := (fvar₂, isTypeOfFVar₂) - overlap := cls } - if clsIdx = 0 && isTypeOfFVar₂ then - break -- Don't consider further projections of this local instance + unless done.contains fvar₂ do + overlaps := overlaps.push { + fvar₁ := (fvar₁, clsIdx = 0) + fvar₂ := (fvar₂, isTypeOfFVar₂) + overlap := cls } + if clsIdx = 0 || isTypeOfFVar₂ then + done := done.push fvar₂ -- Don't consider `fvar₂` any more else insts := insts.insert cls (fvar₁, clsIdx = 0) return overlaps From f034b5d10e2cbc9ee1acff4854080bff2c14d349 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 14:44:18 -0500 Subject: [PATCH 014/141] chore: deriving --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index f75a2dc83e9df3..b69d1e74c57d74 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -52,6 +52,7 @@ structure Overlap where fvar₂ : Expr × Bool /-- A type class on which `fvar₁` and `fvar₂`'s data-carrying projections overlap. -/ overlap : Expr +deriving Inhabited, Repr /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. -/ def findOverlappingDataInstances : MetaM (Array Overlap) := do From b65b2ea0366877c02eab22f51db94f8e5c14204b Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:20:44 -0500 Subject: [PATCH 015/141] refactor: use an ultimately simpler data structure for collecting overlaps --- .../Tactic/Linter/OverlappingInstances.lean | 47 +++++++------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index b69d1e74c57d74..940d31bceac6a3 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -39,26 +39,22 @@ private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := else return acc -/-- An overlap between two local instances. This is introduced for readability, as all fields are -`Expr`s. -/ -structure Overlap where - /-- A local instance free variable whose data-carrying projections overlap with `fvar₂`. The - `Bool` indicates whether `fvar₁ == overlap`; this guides the logic and is used for error - reporting. -/ - fvar₁ : Expr × Bool - /-- A local instance free variable whose data-carrying projections overlap with `fvar₁`. The - `Bool` indicates whether `fvar₂ == overlap`; this guides the logic and is used for error - reporting. -/ - fvar₂ : Expr × Bool - /-- A type class on which `fvar₁` and `fvar₂`'s data-carrying projections overlap. -/ - overlap : Expr -deriving Inhabited, Repr +/-- Stores the local instance overlaps per class. The keys are the class, and the values are local +instances which have the class as a projection. The `Bool` value of each entry indicates whether +its type is exactly the key class. There may be assumed to be at least two local instances per +class. -/ +abbrev Overlaps := ExprMap (ExprMap Bool) + +/-- Inserts an overlap into `Overlaps`. -/ +def Overlaps.insert (cls : Expr) (fvar₁ fvar₂ : Expr × Bool) (overlaps : Overlaps) : Overlaps := + overlaps.alter cls fun map? => + map?.getD ∅ |>.insert fvar₁.1 fvar₁.2 |>.insert fvar₂.1 fvar₂.2 /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. -/ -def findOverlappingDataInstances : MetaM (Array Overlap) := do - let mut overlaps : Array Overlap := #[] - /- The `Bool` indicates whether the given class key is exactly the type of the associated `fvar` - value. This is used for error reporting. -/ +def findOverlappingDataInstances : MetaM Overlaps := do + let mut overlaps : Overlaps := {} + /- Records all instances encountered, and so is distinct from `overlaps`. The `Bool` indicates + whether the given class key is exactly the type of the associated `fvar` value. -/ let mut insts : Std.HashMap Expr (Expr × Bool) := {} for { fvar := fvar₁, .. } in ← getLocalInstances do unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue @@ -74,10 +70,7 @@ def findOverlappingDataInstances : MetaM (Array Overlap) := do let cls := projClasses[clsIdx] if let some (fvar₂, isTypeOfFVar₂) := insts[cls]? then unless done.contains fvar₂ do - overlaps := overlaps.push { - fvar₁ := (fvar₁, clsIdx = 0) - fvar₂ := (fvar₂, isTypeOfFVar₂) - overlap := cls } + overlaps := overlaps.insert cls (fvar₁, clsIdx = 0) (fvar₂, isTypeOfFVar₂) if clsIdx = 0 || isTypeOfFVar₂ then done := done.push fvar₂ -- Don't consider `fvar₂` any more else @@ -138,14 +131,6 @@ def overlappingInstances : Linter where forallTelescope? remainingType? do let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do - /- The fvars overlapping on a given class. For each class belonging to some overlap, - assign to it the fvars which have it as a projection, preserving the data as to - whether these fvars were instances of the class itself (as opposed to projecting to - it) in the `Bool`. -/ - let mut collectedByOverlap : Std.HashMap Expr (Std.HashSet (Expr × Bool)) := {} - for { fvar₁, fvar₂, overlap } in overlaps do - collectedByOverlap := collectedByOverlap.alter overlap fun set? => - (set?.getD ∅).insert fvar₁ |>.insert fvar₂ -- For now, no hovers, since the name clashes with the aux decl of the same name in -- the lctx. TODO: account for this. @@ -156,7 +141,7 @@ def overlappingInstances : Linter where "current declaration"} \ has instance hypotheses which overlap on data-carrying components." - for (overlap, fvars) in collectedByOverlap do + for (overlap, fvars) in overlaps do let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => if isDirect then .inl fvar else .inr fvar let overlapType := m!"`{.ofInstanceType overlap}`" From b3770012894cf3068bae35ca73318f381e5b12d7 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:23:51 -0500 Subject: [PATCH 016/141] docs: explain choices --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 940d31bceac6a3..081f3a8edad517 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -41,8 +41,9 @@ private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := /-- Stores the local instance overlaps per class. The keys are the class, and the values are local instances which have the class as a projection. The `Bool` value of each entry indicates whether -its type is exactly the key class. There may be assumed to be at least two local instances per -class. -/ +its type is exactly the key class. We use an `ExprMap Bool` here instead of e.g. an +`Array (Expr × Bool`) to ensure that each local instance is recorded only once. There may be +assumed to be at least two local instances per class. -/ abbrev Overlaps := ExprMap (ExprMap Bool) /-- Inserts an overlap into `Overlaps`. -/ From 369dd154095b45cb7cffa9d615fb833056a8a7f3 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:32:56 -0500 Subject: [PATCH 017/141] chore: refactor infotree utils slightly --- Mathlib/Lean/Elab/InfoTree.lean | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index 6786dfcbc42d30..40f91b7058dfae 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -99,6 +99,17 @@ def onHighestNode? {α} (t : InfoTree) (ctx? : Option ContextInfo) (f : ContextInfo → Info → PersistentArray InfoTree → α) : Option α := t.findSome? (ctx? := ctx?) fun ctx i ch => some (f ctx i ch) +/-- +Returns the context and `info` on the outermost `.node info _` which has +context, having merged and updated contexts appropriately. + +If `ctx?` is `some ctx`, `ctx` is used as an initial context. A `ctx?` of `none` should **only** be +used when operating on the first node of the entire infotree. Otherwise, it is likely that no +context will be found. +-/ +def getHighestInfo? (t : InfoTree) (ctx? : Option ContextInfo) : Option (ContextInfo × Info) := + t.onHighestNode? ctx? fun ctx i _ => (ctx, i) + /-- Get the `parentDecl`s of every elaborated body. @@ -129,7 +140,7 @@ def getDeclBodyInfos (t : InfoTree) : List (ContextInfo × Info) := if i.value.typeName == ``Lean.Elab.Term.BodyInfo then if h : 0 < body.size then -- See through `.context`s instead of just matching on `.node`: - let result? := body[0].onHighestNode? (ctx? := ctx) fun ctx i _ => (ctx, i) + let result? := body[0].getHighestInfo? ctx if let some result := result? then result :: acc else acc From 04a7e794443779a7cf8b8dd2d01dbcc1041cc01d Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:34:46 -0500 Subject: [PATCH 018/141] clean up `else acc` chain a bit --- Mathlib/Lean/Elab/InfoTree.lean | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index 40f91b7058dfae..d91c6f28ca10e9 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -136,16 +136,14 @@ either of these conditions. -/ def getDeclBodyInfos (t : InfoTree) : List (ContextInfo × Info) := t.foldInfoTree (init := []) fun ctx t acc => match t with - | .node (.ofCustomInfo i) body => + | .node (.ofCustomInfo i) body => Id.run do if i.value.typeName == ``Lean.Elab.Term.BodyInfo then if h : 0 < body.size then -- See through `.context`s instead of just matching on `.node`: let result? := body[0].getHighestInfo? ctx if let some result := result? then - result :: acc - else acc - else acc - else acc + return result :: acc + return acc | _ => acc /-- From 28d793826f45d735828d4e855123a236e55719ac Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:36:56 -0500 Subject: [PATCH 019/141] chore: remove unused variables --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 081f3a8edad517..e74beef4a734a0 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -113,12 +113,7 @@ def overlappingInstances : Linter where pure (decl.lctx, some decl.localInstances, some decl.type) | .ofTermInfo i | .ofPartialTermInfo i => pure (i.lctx, none, i.expectedType?) - | _ => continue -- Unreachable. TODO: refactor? - let namingCtx : NamingContext := { - currNamespace := ← getCurrNamespace - openDecls := ← getOpenDecls - } - let outerEnv ← getEnv + | _ => continue -- Ought to be unreachable. TODO: check or refactor? ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| withRef (← getRef) do /- If there's a remaining expected type, then telescope into it in case it contains more From 691267ddd48a318746ff7560cb483b19d0d4343b Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:38:23 -0500 Subject: [PATCH 020/141] chore: readability --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index e74beef4a734a0..e7344b64de6b5a 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -114,8 +114,9 @@ def overlappingInstances : Linter where | .ofTermInfo i | .ofPartialTermInfo i => pure (i.lctx, none, i.expectedType?) | _ => continue -- Ought to be unreachable. TODO: check or refactor? + let outerRef ← getRef ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| - withRef (← getRef) do + withRef outerRef do /- If there's a remaining expected type, then telescope into it in case it contains more instance hypotheses. For now, we don't use the new fvars or remaining type for anything, but these could be passed to `k`. -/ From e9508ae5fbbf1935db48eae64feb3c0ac6e43866 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:38:56 -0500 Subject: [PATCH 021/141] chore: move comment --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index e7344b64de6b5a..2d65594b71b14c 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -116,6 +116,7 @@ def overlappingInstances : Linter where | _ => continue -- Ought to be unreachable. TODO: check or refactor? let outerRef ← getRef ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| + -- TODO: better logging location withRef outerRef do /- If there's a remaining expected type, then telescope into it in case it contains more instance hypotheses. For now, we don't use the new fvars or remaining type for anything, @@ -158,7 +159,6 @@ def overlappingInstances : Linter where else m!"There are {direct.length} instances of {overlapType} in the local \ context, and it is also provided by {indirectTypes}." - -- TODO: better logging location -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant logWarning <|← addMessageContextFull msg From b60a10514c87d4ff788315045b32d2d3a5f49d19 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 7 Jan 2026 15:49:04 -0500 Subject: [PATCH 022/141] test: show projections --- MathlibTest/OverlappingInstances.lean | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index a4a27dfcb461ff..460bcd2ff36f9d 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -6,7 +6,10 @@ set_option linter.overlappingInstances true namespace Lean -class Bar (α : Type) where +class SubBar (α : Type) where + a' : α + +class Bar (α : Type) extends SubBar α where a : α class Baz (β : Type) where @@ -37,7 +40,9 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- warning: The declaration `Lean.foo'` has instance hypotheses which overlap on data-carrying components. -`[Bar Nat]` is provided by `[Foo' Nat String]` and `[Foo Nat Bool]`. +`[SubBar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. + +`[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. -/ #guard_msgs in def foo' [Foo Nat Bool] [Foo' Nat String] : Bool := by @@ -47,6 +52,8 @@ def foo' [Foo Nat Bool] [Foo' Nat String] : Bool := by /-- warning: The declaration `Lean.foo''` has instance hypotheses which overlap on data-carrying components. +`[SubBar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. + `[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. There are 2 instances of `[Foo Nat Bool]`. From e7616909bbac4e0c9e99bb9f44a1da7366d61971 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:44:06 -0500 Subject: [PATCH 023/141] chore: refactor message generation --- .../Tactic/Linter/OverlappingInstances.lean | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 2d65594b71b14c..873e0daa7a65ee 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -90,6 +90,39 @@ to group and nest appropriately. -/ private def _root_.Lean.MessageData.ofInstanceType (e : Expr) : MessageData := m!"{e}".sbracket +/-- Creates a message from some `Overlaps`, assumed to be nonempty. -/ +def Overlaps.toMsg (ctx : ContextInfo) (overlaps : Overlaps) : MetaM MessageData := do + -- For now, no hovers, since the name clashes with the aux decl of the same name in + -- the lctx. TODO: account for this. + let mut msg := m!"The \ + {if let some decl := ctx.parentDecl? then + m!"declaration `{privateToUserName decl}`" + else + "current declaration"} \ + has instance hypotheses which overlap on data-carrying components." + + for (overlap, fvars) in overlaps do + let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => + if isDirect then .inl fvar else .inr fvar + let overlapType := m!"`{.ofInstanceType overlap}`" + let indirectTypes := MessageData.andList <|← indirect.mapM fun fvar => + return m!"`{.ofInstanceType <|← inferType fvar}`" + msg := msg ++ "\n\n" + msg := msg ++ + if indirect.isEmpty then + -- Necessarily plural: + m!"There are {direct.length} instances of {overlapType}." + else + if direct.isEmpty then + m!"{overlapType} is provided by {indirectTypes}." + else if let [_] := direct then + m!"There is an instance of {overlapType} in the local context, but it is \ + also provided by {indirectTypes}." + else + m!"There are {direct.length} instances of {overlapType} in the local \ + context, and it is also provided by {indirectTypes}." + addMessageContextFull msg + open Linter in /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. @@ -121,7 +154,7 @@ def overlappingInstances : Linter where /- If there's a remaining expected type, then telescope into it in case it contains more instance hypotheses. For now, we don't use the new fvars or remaining type for anything, but these could be passed to `k`. -/ - letI forallTelescope? expectedType? (k : MetaM Unit) := + let forallTelescope? expectedType? (k : MetaM Unit) := if let some type := expectedType? then forallTelescope type fun _ _ => k else @@ -129,37 +162,7 @@ def overlappingInstances : Linter where forallTelescope? remainingType? do let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do - - -- For now, no hovers, since the name clashes with the aux decl of the same name in - -- the lctx. TODO: account for this. - let mut msg := m!"The \ - {if let some decl := ctx.parentDecl? then - m!"declaration `{privateToUserName decl}`" - else - "current declaration"} \ - has instance hypotheses which overlap on data-carrying components." - - for (overlap, fvars) in overlaps do - let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => - if isDirect then .inl fvar else .inr fvar - let overlapType := m!"`{.ofInstanceType overlap}`" - let indirectTypes := MessageData.andList <|← indirect.mapM fun fvar => - return m!"`{.ofInstanceType <|← inferType fvar}`" - msg := msg ++ "\n\n" - msg := msg ++ - if indirect.isEmpty then - -- Necessarily plural: - m!"There are {direct.length} instances of {overlapType}." - else - if direct.isEmpty then - m!"{overlapType} is provided by {indirectTypes}." - else if let [direct] := direct then - m!"There is an instance of {overlapType} in the local context, but it is \ - also provided by {indirectTypes}." - else - m!"There are {direct.length} instances of {overlapType} in the local \ - context, and it is also provided by {indirectTypes}." -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant - logWarning <|← addMessageContextFull msg + logWarning <|← overlaps.toMsg ctx initialize addLinter overlappingInstances From c77a8d3384ad3ff5d71cdcf6e01f727e030be422 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Fri, 23 Jan 2026 15:27:57 -0500 Subject: [PATCH 024/141] chore: continue refactoring --- .../Tactic/Linter/OverlappingInstances.lean | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 873e0daa7a65ee..4c807fae145d20 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -90,17 +90,24 @@ to group and nest appropriately. -/ private def _root_.Lean.MessageData.ofInstanceType (e : Expr) : MessageData := m!"{e}".sbracket -/-- Creates a message from some `Overlaps`, assumed to be nonempty. -/ -def Overlaps.toMsg (ctx : ContextInfo) (overlaps : Overlaps) : MetaM MessageData := do - -- For now, no hovers, since the name clashes with the aux decl of the same name in - -- the lctx. TODO: account for this. - let mut msg := m!"The \ - {if let some decl := ctx.parentDecl? then - m!"declaration `{privateToUserName decl}`" - else - "current declaration"} \ - has instance hypotheses which overlap on data-carrying components." +/-- +Creates a description of the current declaration in messages: "declaration " if the `parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". +TODO: For now, this does not produce hovers on ``, since the name may clash with the aux +decl of the same name in the local context. In the future, we should account for this, and render +the name within a more appropriate message context. The type of this declaration is therefore +subject to change. +-/ +def _root_.Lean.Elab.ContextInfo.toDeclDescr (ctx : ContextInfo) : MessageData := + if let some decl := ctx.parentDecl? then + m!"declaration `{privateToUserName decl}`" + else + "current declaration" + +/-- Creates a message describing the violations captured in `Overlaps`, assumed to be nonempty. -/ +def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM MessageData := do + let mut msg := m!"The {declDescr} \ + has instance hypotheses which overlap on data-carrying components." for (overlap, fvars) in overlaps do let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => if isDirect then .inl fvar else .inr fvar @@ -154,15 +161,15 @@ def overlappingInstances : Linter where /- If there's a remaining expected type, then telescope into it in case it contains more instance hypotheses. For now, we don't use the new fvars or remaining type for anything, but these could be passed to `k`. -/ - let forallTelescope? expectedType? (k : MetaM Unit) := - if let some type := expectedType? then + let forallTelescopeRemainingType (k : MetaM Unit) := + if let some type := remainingType? then forallTelescope type fun _ _ => k else k - forallTelescope? remainingType? do + forallTelescopeRemainingType do let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant - logWarning <|← overlaps.toMsg ctx + logWarning <|← overlaps.toMsg ctx.toDeclDescr initialize addLinter overlappingInstances From f86ae520ae72356e423032095b8a08e3f7ad5b2c Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:00:05 -0500 Subject: [PATCH 025/141] feat: first pass at deduplicating error messages --- .../Tactic/Linter/OverlappingInstances.lean | 96 +++++++++++++------ 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 4c807fae145d20..99b3671b737c17 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -21,28 +21,53 @@ public meta section namespace Mathlib.Tactic.OverlappingInstances -/-- Given an instance `e`, conpute return all data carrying classes that are -the type of `e` itself, or a child class. -/ -private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := #[]) : - StateRefT NameSet MetaM (Array Expr) := do +/-- Given an instance `e`, compute all data carrying classes that are +the type of `e` itself, or a child class, together with the indices of the parents of each +projection as they appear in this array. -/ +private partial def getStructureDataProjections (e : Expr) (acc : Array (List Nat × Expr) := #[]) + (parentIdx? : Option Nat := none) : + StateRefT (NameMap (Option Nat)) MetaM (Array (List Nat × Expr)) := do let eType ← whnf (← inferType e) if ← isProp eType then return acc let .const structName us := eType.getForallBody.getAppFn | throwError "{e} is not an instance of a structure" - if (← get).contains structName then return acc - modify (·.insert structName) - let some info := getStructureInfo? (← getEnv) structName | return acc - info.parentInfo.foldlM (init := acc.push eType) fun acc info ↦ do - if ← isInstance info.projFn then - let proj := Expr.app (mkAppN (.const info.projFn us) eType.getAppArgs) e - getStructureDataProjections proj acc - else - return acc + if let some structIdx? := (← get).get? structName then + if let some structIdx := structIdx? then -- `structName` may not have actually been a structure + if let some parentIdx := parentIdx? then + /- `e` has already been recorded, but is being encountered as the child of a new parent at + `parentIdx`. Add this parent index to `e`'s original parent indices. -/ + return acc.modify structIdx fun (parents, struct) => (parentIdx :: parents, struct) + return acc + if let some info := getStructureInfo? (← getEnv) structName then + let currentIdx := acc.size + -- Record the index at which this structure occurs in `acc`, so we can add to its parents later + -- if it is encountered as a projection of something else. + modify (·.insert structName currentIdx) + info.parentInfo.foldlM (init := acc.push (parentIdx?.toList, eType)) fun acc info ↦ do + if ← isInstance info.projFn then + let proj := Expr.app (mkAppN (.const info.projFn us) eType.getAppArgs) e + getStructureDataProjections proj acc currentIdx + else + return acc + else + -- Record that we've encountered this constant; however, it is not in the array. + modify (·.insert structName none) + return acc + +/-- Given an array of projection types paired with indices for their parents, returns `true` if `p` +is true for the types at any of the starting indices or their transitive parents. -/ +private partial def hasParentP! (projections : Array (List Nat × Expr)) (p : Nat → Expr → Bool) + (startingIdxs : List Nat) : Bool := + match startingIdxs with + | [] => false + | idx :: idxs => + let (parentIdxs, expr) := projections[idx]! + p idx expr || hasParentP! projections p parentIdxs || hasParentP! projections p idxs /-- Stores the local instance overlaps per class. The keys are the class, and the values are local instances which have the class as a projection. The `Bool` value of each entry indicates whether its type is exactly the key class. We use an `ExprMap Bool` here instead of e.g. an -`Array (Expr × Bool`) to ensure that each local instance is recorded only once. There may be +`Array (Expr × Bool)` to ensure that each local instance is recorded only once. There may be assumed to be at least two local instances per class. -/ abbrev Overlaps := ExprMap (ExprMap Bool) @@ -51,31 +76,40 @@ def Overlaps.insert (cls : Expr) (fvar₁ fvar₂ : Expr × Bool) (overlaps : Ov overlaps.alter cls fun map? => map?.getD ∅ |>.insert fvar₁.1 fvar₁.2 |>.insert fvar₂.1 fvar₂.2 +/-- Returns `true` iff `fvar₁` and `fvar₂` overlap on the `cls` projection typeclass. -/ +def Overlaps.containsOverlapOn (fvar₁ fvar₂ : Expr) (cls : Expr) (overlaps : Overlaps) : Bool := + match overlaps[cls]? with + | none => false + | some overlap => overlap.contains fvar₁ && overlap.contains fvar₂ + /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. -/ def findOverlappingDataInstances : MetaM Overlaps := do let mut overlaps : Overlaps := {} - /- Records all instances encountered, and so is distinct from `overlaps`. The `Bool` indicates - whether the given class key is exactly the type of the associated `fvar` value. -/ + /- Associates all (data-carrying) typeclasses encountered to the first fvar that had a projection + into this given typeclass. Since it records all typeclasses indiscriminately, it is distinct from + `overlaps`. The `Bool` indicates whether the given class key is exactly the type of the + associated `fvar` value. We use this for error reporting. -/ let mut insts : Std.HashMap Expr (Expr × Bool) := {} for { fvar := fvar₁, .. } in ← getLocalInstances do unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue let projClasses ← forallTelescope (← inferType fvar₁) fun xs _ ↦ do - (← getStructureDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM (mkForallFVars xs) - /- The fvars that are either projections of the current fvar or have projections equal to the - current fvar. In both cases we want to ignore further matches against these fvars. - - For less verbose error reporting, we would ideally also ignore overlaps which share a parent; - we may eventually want a different data structure for `projClasses` for this. -/ - let mut done : Array Expr := #[] - for h : clsIdx in 0...projClasses.size do - let cls := projClasses[clsIdx] + (← getStructureDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM fun (parentIdx?, expr) => + return (parentIdx?, ← mkForallFVars xs expr) + for (parentIdxs, cls) in projClasses, idx in 0...* do if let some (fvar₂, isTypeOfFVar₂) := insts[cls]? then - unless done.contains fvar₂ do - overlaps := overlaps.insert cls (fvar₁, clsIdx = 0) (fvar₂, isTypeOfFVar₂) - if clsIdx = 0 || isTypeOfFVar₂ then - done := done.push fvar₂ -- Don't consider `fvar₂` any more + -- We have encountered a projection with this type already; we should now record an overlap, + -- unless it is (or will) be redundant. + -- Note that the actions in this branch are allowed to be "slow". + let shouldIgnoreCurrent (parentIdx : Nat) (parentClass : Expr) := + -- If a parent further on in `projClasses` will overlap via `fvar₂`, ignore this child. + -- Note that we can assume `false`, as only the first array element has `true`. + ((idx < parentIdx) && insts[parentClass]?.isEqSome (fvar₂, false)) + -- If `fvar₁` and `fvar₂` already overlap on a parent, ignore this redundant overlap. + || overlaps.containsOverlapOn fvar₁ fvar₂ parentClass + unless hasParentP! projClasses shouldIgnoreCurrent parentIdxs do + overlaps := overlaps.insert cls (fvar₁, parentIdxs.isEmpty) (fvar₂, isTypeOfFVar₂) else - insts := insts.insert cls (fvar₁, clsIdx = 0) + insts := insts.insert cls (fvar₁, parentIdxs.isEmpty) return overlaps /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ @@ -98,7 +132,7 @@ decl of the same name in the local context. In the future, we should account for the name within a more appropriate message context. The type of this declaration is therefore subject to change. -/ -def _root_.Lean.Elab.ContextInfo.toDeclDescr (ctx : ContextInfo) : MessageData := +private def _root_.Lean.Elab.ContextInfo.toDeclDescr (ctx : ContextInfo) : MessageData := if let some decl := ctx.parentDecl? then m!"declaration `{privateToUserName decl}`" else From fe0dfa5720814ff8222c36f21448779e4f1fcd64 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:07:03 -0500 Subject: [PATCH 026/141] chore: tweak code comments --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 99b3671b737c17..1dd4a6080258ef 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -34,8 +34,8 @@ private partial def getStructureDataProjections (e : Expr) (acc : Array (List Na if let some structIdx? := (← get).get? structName then if let some structIdx := structIdx? then -- `structName` may not have actually been a structure if let some parentIdx := parentIdx? then - /- `e` has already been recorded, but is being encountered as the child of a new parent at - `parentIdx`. Add this parent index to `e`'s original parent indices. -/ + -- `e` has already been recorded, but is being encountered as the child of a new parent at + -- `parentIdx`. Add this parent index to `e`'s original parent indices. return acc.modify structIdx fun (parents, struct) => (parentIdx :: parents, struct) return acc if let some info := getStructureInfo? (← getEnv) structName then @@ -85,10 +85,11 @@ def Overlaps.containsOverlapOn (fvar₁ fvar₂ : Expr) (cls : Expr) (overlaps : /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. -/ def findOverlappingDataInstances : MetaM Overlaps := do let mut overlaps : Overlaps := {} - /- Associates all (data-carrying) typeclasses encountered to the first fvar that had a projection - into this given typeclass. Since it records all typeclasses indiscriminately, it is distinct from - `overlaps`. The `Bool` indicates whether the given class key is exactly the type of the - associated `fvar` value. We use this for error reporting. -/ + /- Associates all (data-carrying) typeclasses encountered to the first fvar that has a projection + into this given typeclass, allowing us to detect when a projection has been seen before. + + The `Bool` indicates whether the given class key is exactly the type of the associated fvar + value. We use this for error reporting. -/ let mut insts : Std.HashMap Expr (Expr × Bool) := {} for { fvar := fvar₁, .. } in ← getLocalInstances do unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue @@ -106,6 +107,8 @@ def findOverlappingDataInstances : MetaM Overlaps := do ((idx < parentIdx) && insts[parentClass]?.isEqSome (fvar₂, false)) -- If `fvar₁` and `fvar₂` already overlap on a parent, ignore this redundant overlap. || overlaps.containsOverlapOn fvar₁ fvar₂ parentClass + -- See if any parent of the current projection, starting with the immediate `parentIdxs`, + -- imply it is redundant. unless hasParentP! projClasses shouldIgnoreCurrent parentIdxs do overlaps := overlaps.insert cls (fvar₁, parentIdxs.isEmpty) (fvar₂, isTypeOfFVar₂) else From 6397f9f3bd6351d238f9a492fe0510d36e532059 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:07:19 -0500 Subject: [PATCH 027/141] chore: update tests to remove redundant messages --- MathlibTest/OverlappingInstances.lean | 4 ---- 1 file changed, 4 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 460bcd2ff36f9d..ef8b870a2c06a8 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -40,8 +40,6 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- warning: The declaration `Lean.foo'` has instance hypotheses which overlap on data-carrying components. -`[SubBar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. - `[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. -/ #guard_msgs in @@ -52,8 +50,6 @@ def foo' [Foo Nat Bool] [Foo' Nat String] : Bool := by /-- warning: The declaration `Lean.foo''` has instance hypotheses which overlap on data-carrying components. -`[SubBar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. - `[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. There are 2 instances of `[Foo Nat Bool]`. From b50ed2c5e6f02b020f357ea7ac2067eed85b0cc8 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:11:07 -0500 Subject: [PATCH 028/141] chore: improve tests a bit (still needs some fleshing out) --- MathlibTest/OverlappingInstances.lean | 51 +++++++++++++-------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index ef8b870a2c06a8..2ac9cd7a387b9f 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -18,10 +18,11 @@ class Baz (β : Type) where class Baq (β : Type) where b : β -class Foo (α) (β) extends Bar α, Baz β +class FooBarBaz (α) extends Bar α, Baz α +class FooBarBaz' (α) extends Bar α, Baz α -class Foo' (α) (β) extends Bar α, Baq β +class FooBarBaq (α) extends Bar α, Baq α /-- error: unsolved goals @@ -38,53 +39,49 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- -warning: The declaration `Lean.foo'` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `Lean.foo₁` has instance hypotheses which overlap on data-carrying components. -`[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. +`[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. -/ #guard_msgs in -def foo' [Foo Nat Bool] [Foo' Nat String] : Bool := by +def foo₁ [FooBarBaz Nat] [FooBarBaq Nat] : Bool := by exact true - /-- -warning: The declaration `Lean.foo''` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `Lean.foo₂` has instance hypotheses which overlap on data-carrying components. -`[Bar Nat]` is provided by `[Foo Nat Bool]` and `[Foo' Nat String]`. +There are 2 instances of `[FooBarBaz Nat]`. -There are 2 instances of `[Foo Nat Bool]`. +`[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. -/ #guard_msgs in -def foo'' [Foo Nat Bool] [Foo Nat Bool] [Foo' Nat String] : Bool := true +def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- -warning: The declaration `Lean.foo'''` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `Lean.foo₃` has instance hypotheses which overlap on data-carrying components. -There are 2 instances of `[Foo Nat Bool]`. +There are 2 instances of `[FooBarBaz Nat]`. -/ #guard_msgs in -def foo''' [Foo Nat Bool] [Foo Nat Bool] : Bool := true +def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- -error: Failed to infer type of definition `foo''''` ---- -warning: The declaration `Lean.foo''''` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `Lean.foo₄` has instance hypotheses which overlap on data-carrying components. -There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[Foo Nat Bool]`. +There are 2 instances of `[FooBarBaz Nat]`. -There are 2 instances of `[Foo Nat Bool]`. +There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[FooBarBaz Nat]`. -/ #guard_msgs in -def foo'''' [Foo Nat Bool] [Foo Nat Bool] [Bar Nat] := sorry +def foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : Bool := true --- Correct? Might not have `Bar Nat` if we can't provide `α`, but might if we can. --- Only needs `(usedOnly := true)` in `mkForallFVars` to change behavior. +-- Note that `[SubBar Nat]` is absent, as `[Bar Nat]` is already reported. /-- -error: unsolved goals -inst✝¹ : Foo Nat Bool -inst✝ : (α : Type) → Foo' Nat α -⊢ Bool +warning: The declaration `Lean.foo₅` has instance hypotheses which overlap on data-carrying components. + +`[Baz Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. + +`[Bar Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. -/ #guard_msgs in -def fooForall [Foo Nat Bool] [∀ α, Foo' Nat α] : Bool := by - skip +def foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : Bool := true From 490dd496d1cea52f7868db0313eed082f6a4f22b Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 28 Jan 2026 23:12:04 -0500 Subject: [PATCH 029/141] chore: variable names --- MathlibTest/OverlappingInstances.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 2ac9cd7a387b9f..1f8fdffbd68ba6 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -12,11 +12,11 @@ class SubBar (α : Type) where class Bar (α : Type) extends SubBar α where a : α -class Baz (β : Type) where - b : β +class Baz (α : Type) where + b : α -class Baq (β : Type) where - b : β +class Baq (α : Type) where + b : α class FooBarBaz (α) extends Bar α, Baz α From 0aaa23cf7c614f40520a47653063974e034a6dcb Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:53:58 -0500 Subject: [PATCH 030/141] chore: improve message --- .../Tactic/Linter/OverlappingInstances.lean | 35 ++++++++++++------- MathlibTest/OverlappingInstances.lean | 33 ++++++++++------- 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 1dd4a6080258ef..75418d036c7279 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -135,36 +135,45 @@ decl of the same name in the local context. In the future, we should account for the name within a more appropriate message context. The type of this declaration is therefore subject to change. -/ -private def _root_.Lean.Elab.ContextInfo.toDeclDescr (ctx : ContextInfo) : MessageData := +private def _root_.Lean.Elab.ContextInfo.toDeclDescr (ctx : ContextInfo) : MetaM MessageData := do if let some decl := ctx.parentDecl? then - m!"declaration `{privateToUserName decl}`" + let decl ← unresolveNameGlobal decl + return m!"declaration `{decl}`" else - "current declaration" + return "current declaration" /-- Creates a message describing the violations captured in `Overlaps`, assumed to be nonempty. -/ def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM MessageData := do let mut msg := m!"The {declDescr} \ - has instance hypotheses which overlap on data-carrying components." + has instance hypotheses which conflict on the data they provide. Specifically:" + let mut msgs := #[] for (overlap, fvars) in overlaps do let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => if isDirect then .inl fvar else .inr fvar let overlapType := m!"`{.ofInstanceType overlap}`" let indirectTypes := MessageData.andList <|← indirect.mapM fun fvar => return m!"`{.ofInstanceType <|← inferType fvar}`" - msg := msg ++ "\n\n" - msg := msg ++ + msgs := msgs.push <| if indirect.isEmpty then -- Necessarily plural: m!"There are {direct.length} instances of {overlapType}." else - if direct.isEmpty then - m!"{overlapType} is provided by {indirectTypes}." - else if let [_] := direct then - m!"There is an instance of {overlapType} in the local context, but it is \ + match direct with + | [] => m!"{overlapType} is provided by {indirectTypes}." + | [_] => m!"There is an instance of {overlapType} in the local context, but it is \ also provided by {indirectTypes}." - else - m!"There are {direct.length} instances of {overlapType} in the local \ + | _ => m!"There are {direct.length} instances of {overlapType} in the local \ context, and it is also provided by {indirectTypes}." + + msg := + if h : msgs.size = 1 then + msg ++ "\n\n" ++ msgs[0] + else + msgs.foldl (init := msg ++ "\n") fun accMsg newMsg => m!"{accMsg}\n• {newMsg}" + + msg := msg ++ m!"\n\n\ + There should only be a single instance of these data-carrying typeclasses in the local context \ + at a time. Consider choosing different instance hypotheses for the {declDescr}." addMessageContextFull msg open Linter in @@ -207,6 +216,6 @@ def overlappingInstances : Linter where let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant - logWarning <|← overlaps.toMsg ctx.toDeclDescr + logWarning <|← overlaps.toMsg <|← ctx.toDeclDescr initialize addLinter overlappingInstances diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 1f8fdffbd68ba6..f5966a8ec3a804 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -29,9 +29,11 @@ error: unsolved goals inst✝¹ inst✝ : Add Nat ⊢ [Add Nat] → [Add Nat] → Bool --- -warning: The declaration `Lean.foo` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `foo` has instance hypotheses which conflict on the data they provide. Specifically: There are 4 instances of `[Add Nat]`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo`. -/ #guard_msgs in def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @@ -39,49 +41,56 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- -warning: The declaration `Lean.foo₁` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `foo₁` has instance hypotheses which conflict on the data they provide. Specifically: `[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₁`. -/ #guard_msgs in def foo₁ [FooBarBaz Nat] [FooBarBaq Nat] : Bool := by exact true /-- -warning: The declaration `Lean.foo₂` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `foo₂` has instance hypotheses which conflict on the data they provide. Specifically: -There are 2 instances of `[FooBarBaz Nat]`. +• There are 2 instances of `[FooBarBaz Nat]`. +• `[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. -`[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₂`. -/ #guard_msgs in def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- -warning: The declaration `Lean.foo₃` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `foo₃` has instance hypotheses which conflict on the data they provide. Specifically: There are 2 instances of `[FooBarBaz Nat]`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₃`. -/ #guard_msgs in def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- -warning: The declaration `Lean.foo₄` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `foo₄` has instance hypotheses which conflict on the data they provide. Specifically: -There are 2 instances of `[FooBarBaz Nat]`. +• There are 2 instances of `[FooBarBaz Nat]`. +• There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[FooBarBaz Nat]`. -There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[FooBarBaz Nat]`. +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₄`. -/ #guard_msgs in def foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : Bool := true -- Note that `[SubBar Nat]` is absent, as `[Bar Nat]` is already reported. /-- -warning: The declaration `Lean.foo₅` has instance hypotheses which overlap on data-carrying components. +warning: The declaration `foo₅` has instance hypotheses which conflict on the data they provide. Specifically: -`[Baz Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. +• `[Baz Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. +• `[Bar Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. -`[Bar Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₅`. -/ #guard_msgs in def foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : Bool := true From f258f91846f32ffd3d7e227219047c3b09a93309 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:57:26 -0500 Subject: [PATCH 031/141] chore: maybe improve wording a bit? hmm --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- MathlibTest/OverlappingInstances.lean | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 75418d036c7279..a9a42edd425c23 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -145,7 +145,7 @@ private def _root_.Lean.Elab.ContextInfo.toDeclDescr (ctx : ContextInfo) : MetaM /-- Creates a message describing the violations captured in `Overlaps`, assumed to be nonempty. -/ def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM MessageData := do let mut msg := m!"The {declDescr} \ - has instance hypotheses which conflict on the data they provide. Specifically:" + has instance hypotheses which provide conflicting versions of the same data. Specifically:" let mut msgs := #[] for (overlap, fvars) in overlaps do let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index f5966a8ec3a804..3e95e2c3c245cf 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -29,7 +29,7 @@ error: unsolved goals inst✝¹ inst✝ : Add Nat ⊢ [Add Nat] → [Add Nat] → Bool --- -warning: The declaration `foo` has instance hypotheses which conflict on the data they provide. Specifically: +warning: The declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: There are 4 instances of `[Add Nat]`. @@ -41,7 +41,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- -warning: The declaration `foo₁` has instance hypotheses which conflict on the data they provide. Specifically: +warning: The declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. @@ -52,7 +52,7 @@ def foo₁ [FooBarBaz Nat] [FooBarBaq Nat] : Bool := by exact true /-- -warning: The declaration `foo₂` has instance hypotheses which conflict on the data they provide. Specifically: +warning: The declaration `foo₂` has instance hypotheses which provide conflicting versions of the same data. Specifically: • There are 2 instances of `[FooBarBaz Nat]`. • `[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. @@ -63,7 +63,7 @@ There should only be a single instance of these data-carrying typeclasses in the def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- -warning: The declaration `foo₃` has instance hypotheses which conflict on the data they provide. Specifically: +warning: The declaration `foo₃` has instance hypotheses which provide conflicting versions of the same data. Specifically: There are 2 instances of `[FooBarBaz Nat]`. @@ -73,7 +73,7 @@ There should only be a single instance of these data-carrying typeclasses in the def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- -warning: The declaration `foo₄` has instance hypotheses which conflict on the data they provide. Specifically: +warning: The declaration `foo₄` has instance hypotheses which provide conflicting versions of the same data. Specifically: • There are 2 instances of `[FooBarBaz Nat]`. • There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[FooBarBaz Nat]`. @@ -85,7 +85,7 @@ def foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : Bool := true -- Note that `[SubBar Nat]` is absent, as `[Bar Nat]` is already reported. /-- -warning: The declaration `foo₅` has instance hypotheses which conflict on the data they provide. Specifically: +warning: The declaration `foo₅` has instance hypotheses which provide conflicting versions of the same data. Specifically: • `[Baz Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. • `[Bar Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. From d855595a3b0d103e30827117fc8768ed0c3cdb05 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:10:24 -0500 Subject: [PATCH 032/141] chore: clearer source code + "both" in message --- .../Tactic/Linter/OverlappingInstances.lean | 24 +++++++++++-------- MathlibTest/OverlappingInstances.lean | 8 +++---- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a9a42edd425c23..b3b59bcbd0b6f1 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -148,22 +148,26 @@ def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM Messa has instance hypotheses which provide conflicting versions of the same data. Specifically:" let mut msgs := #[] for (overlap, fvars) in overlaps do - let (direct, indirect) := fvars.toList.partitionMap fun (fvar, isDirect) => - if isDirect then .inl fvar else .inr fvar + let (instsOfOverlap, parentsOfOverlap) := + fvars.toList.partitionMap fun (fvar, isFVarType) => + if isFVarType then .inl fvar else .inr fvar let overlapType := m!"`{.ofInstanceType overlap}`" - let indirectTypes := MessageData.andList <|← indirect.mapM fun fvar => + let parentTypesOfOverlap := MessageData.andList <|← parentsOfOverlap.mapM fun fvar => return m!"`{.ofInstanceType <|← inferType fvar}`" + let parentTypesOfOverlap := + m!"{if let [_, _] := parentsOfOverlap then m!"both " else m!""}{parentTypesOfOverlap}" + msgs := msgs.push <| - if indirect.isEmpty then + if parentsOfOverlap.isEmpty then -- Necessarily plural: - m!"There are {direct.length} instances of {overlapType}." + m!"There are {instsOfOverlap.length} instances of {overlapType}." else - match direct with - | [] => m!"{overlapType} is provided by {indirectTypes}." + match instsOfOverlap with + | [] => m!"{overlapType} is provided by {parentTypesOfOverlap}." | [_] => m!"There is an instance of {overlapType} in the local context, but it is \ - also provided by {indirectTypes}." - | _ => m!"There are {direct.length} instances of {overlapType} in the local \ - context, and it is also provided by {indirectTypes}." + also provided by {parentTypesOfOverlap}." + | _ => m!"There are {instsOfOverlap.length} instances of {overlapType} in the local \ + context, and it is also provided by {parentTypesOfOverlap}." msg := if h : msgs.size = 1 then diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 3e95e2c3c245cf..b132394c90848d 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -43,7 +43,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- warning: The declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: -`[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. +`[Bar Nat]` is provided by both `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₁`. -/ @@ -55,7 +55,7 @@ def foo₁ [FooBarBaz Nat] [FooBarBaq Nat] : Bool := by warning: The declaration `foo₂` has instance hypotheses which provide conflicting versions of the same data. Specifically: • There are 2 instances of `[FooBarBaz Nat]`. -• `[Bar Nat]` is provided by `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. +• `[Bar Nat]` is provided by both `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₂`. -/ @@ -87,8 +87,8 @@ def foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : Bool := true /-- warning: The declaration `foo₅` has instance hypotheses which provide conflicting versions of the same data. Specifically: -• `[Baz Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. -• `[Bar Nat]` is provided by `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. +• `[Baz Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. +• `[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₅`. -/ From 8e54ead13fbfcd42eed84fd8f860ddefea21fe7d Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:15:04 -0500 Subject: [PATCH 033/141] chore: more code cleanup --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index b3b59bcbd0b6f1..12c3050c288bbe 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -168,13 +168,9 @@ def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM Messa also provided by {parentTypesOfOverlap}." | _ => m!"There are {instsOfOverlap.length} instances of {overlapType} in the local \ context, and it is also provided by {parentTypesOfOverlap}." - - msg := - if h : msgs.size = 1 then - msg ++ "\n\n" ++ msgs[0] - else - msgs.foldl (init := msg ++ "\n") fun accMsg newMsg => m!"{accMsg}\n• {newMsg}" - + -- Create a bulleted list if there are multiple messages, otherwise just a single line + msg := if h : msgs.size = 1 then msg ++ "\n\n" ++ msgs[0] else + msgs.foldl (init := msg ++ "\n") fun accMsg newMsg => m!"{accMsg}\n• {newMsg}" msg := msg ++ m!"\n\n\ There should only be a single instance of these data-carrying typeclasses in the local context \ at a time. Consider choosing different instance hypotheses for the {declDescr}." From bea6931be5add1d1e4c149d4ac53e8175d6942ab Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Thu, 29 Jan 2026 14:17:23 -0500 Subject: [PATCH 034/141] test: namespace test --- MathlibTest/OverlappingInstances.lean | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index b132394c90848d..fbe80fd336db41 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -94,3 +94,19 @@ There should only be a single instance of these data-carrying typeclasses in the -/ #guard_msgs in def foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : Bool := true + +namespace Foo + +/-! Test unresolving name (`foo`, not `Foo.foo` or `_private...foo`) -/ + +/-- +warning: The declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: + +There are 2 instances of `[Add Nat]`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo`. +-/ +#guard_msgs in +private def foo [Add Nat] [Add Nat] : Bool := true + +end Foo From b7ceb993a316a504b76ee450c329951a416f5be6 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 31 Jan 2026 16:01:15 -0500 Subject: [PATCH 035/141] chore: refactor info management. lets us document things better. * naming could maybe use improvement; not fully descriptive. * however...`LCtx` is a little more discoverable than e.g. `ProofState` and less bulky than e.g. `LCtxAndExpectedType` --- Mathlib/Lean/Elab/InfoTree.lean | 21 ++++++++++++++++++- .../Tactic/Linter/OverlappingInstances.lean | 16 ++++++-------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index d91c6f28ca10e9..3bd27f5525b29d 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -48,6 +48,8 @@ where namespace InfoTree + + /-- Finds the first result of `← f ctx info children` which is `some a`, descending the tree from the top. Merges and updates contexts as it descends the tree. @@ -154,4 +156,21 @@ using the keyword `theorem` directly. def getTheorems (t : InfoTree) (env : Environment) : List ConstantVal := t.getDeclsByBody.filterMap env.findTheoremConstVal? -end Lean.Elab.InfoTree +end InfoTree + +namespace Info + +/-- Gets the local context, the local instances if available, and the expected type just before +`Expr`. Handles `TacticInfo`s (looking at the first goal), `TermInfo`s, and `PartialTermInfo`s. +Does not get the metavariable context; assumes that the caller has accumulated an ambient +`ContextInfo` at this point which is sufficient. -/ +def getLCtxBefore? : Info → Option (LocalContext × Option LocalInstances × Option Expr) + | .ofTacticInfo i => do + let g ← i.goalsBefore.head? + let decl ← i.mctxBefore.findDecl? g + some (decl.lctx, decl.localInstances, decl.type) + | .ofTermInfo i + | .ofPartialTermInfo i => some (i.lctx, none, i.expectedType?) + | _ => none + +end Lean.Elab.Info diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 12c3050c288bbe..449d1acccbb65f 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -19,6 +19,9 @@ open Lean Meta Elab public meta section +open Lean Elab Command + + namespace Mathlib.Tactic.OverlappingInstances /-- Given an instance `e`, compute all data carrying classes that are @@ -191,18 +194,11 @@ def overlappingInstances : Linter where -- Note: we don't break on errors; we want to lint even on partial declarations for t in ← getInfoTrees do for (ctx, info) in t.getDeclBodyInfos do - let (lctx, localInstances, remainingType?) ← do - match info with - | .ofTacticInfo i => do - let g :: _ := i.goalsBefore | continue - let some decl := i.mctxBefore.findDecl? g | continue - pure (decl.lctx, some decl.localInstances, some decl.type) - | .ofTermInfo i - | .ofPartialTermInfo i => pure (i.lctx, none, i.expectedType?) - | _ => continue -- Ought to be unreachable. TODO: check or refactor? + let some (lctx, localInstances, remainingType?) := info.getLCtxBefore? + | continue + -- TODO: better logging location let outerRef ← getRef ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| - -- TODO: better logging location withRef outerRef do /- If there's a remaining expected type, then telescope into it in case it contains more instance hypotheses. For now, we don't use the new fvars or remaining type for anything, From f381e61fbd934523b2e152fc1d9c28de0d839e14 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:16:37 -0500 Subject: [PATCH 036/141] style: use `Option.elim`, even though I'd prefer a dedicated wrapper --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 449d1acccbb65f..008e93afd1caa5 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -201,14 +201,8 @@ def overlappingInstances : Linter where ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| withRef outerRef do /- If there's a remaining expected type, then telescope into it in case it contains more - instance hypotheses. For now, we don't use the new fvars or remaining type for anything, - but these could be passed to `k`. -/ - let forallTelescopeRemainingType (k : MetaM Unit) := - if let some type := remainingType? then - forallTelescope type fun _ _ => k - else - k - forallTelescopeRemainingType do + instance hypotheses. For now, we don't use the new fvars or return type for anything. -/ + remainingType?.elim id (forallTelescope · fun _ _ => ·) do let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant From e8b9c9737aefa604088ece0f3175527d80fed823 Mon Sep 17 00:00:00 2001 From: "Thomas R. Murrills" <68410468+thorimur@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:10:08 -0500 Subject: [PATCH 037/141] chore: remove accidental whitespace --- Mathlib/Lean/Elab/InfoTree.lean | 2 -- 1 file changed, 2 deletions(-) diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index 3bd27f5525b29d..4b184385c950b8 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -48,8 +48,6 @@ where namespace InfoTree - - /-- Finds the first result of `← f ctx info children` which is `some a`, descending the tree from the top. Merges and updates contexts as it descends the tree. From 949c0f2dcd5be6aaac6ca454c37e305760892fd7 Mon Sep 17 00:00:00 2001 From: "Thomas R. Murrills" <68410468+thorimur@users.noreply.github.com> Date: Sun, 1 Feb 2026 14:11:37 -0500 Subject: [PATCH 038/141] chore: whitespace, opens, namespaces --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 008e93afd1caa5..c7a577e5dd9468 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -15,13 +15,10 @@ public meta import Mathlib.Lean.ContextInfo We want to avoid this because this lead to instance diamonds -/ -open Lean Meta Elab +open Lean Meta Elab Command public meta section -open Lean Elab Command - - namespace Mathlib.Tactic.OverlappingInstances /-- Given an instance `e`, compute all data carrying classes that are @@ -209,3 +206,5 @@ def overlappingInstances : Linter where logWarning <|← overlaps.toMsg <|← ctx.toDeclDescr initialize addLinter overlappingInstances + +end Mathlib.Tactic.OverlappingInstances From 529b6a13271c24eb7f8e805294b7764745730b0e Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 17:09:56 -0500 Subject: [PATCH 039/141] chore: rename `insts` and document more --- .../Tactic/Linter/OverlappingInstances.lean | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index c7a577e5dd9468..481a659cc59570 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -82,7 +82,14 @@ def Overlaps.containsOverlapOn (fvar₁ fvar₂ : Expr) (cls : Expr) (overlaps : | none => false | some overlap => overlap.contains fvar₁ && overlap.contains fvar₂ -/-- Find data-carrying overlaps between the current local instances of the `MetaM` context. -/ +/-- +Find data-carrying overlaps between the current local instances of the `MetaM` context. + +The resulting `Overlaps` can be assumed to have at least two fvars present for each recorded class. +Further, it will only record overlaps at "maximal" nodes in the projection DAG; for example, if +`fvar₁` and `fvar₂` overlap on `cls`, we will not redundantly record their overlap on any +projection `cls.proj`. +-/ def findOverlappingDataInstances : MetaM Overlaps := do let mut overlaps : Overlaps := {} /- Associates all (data-carrying) typeclasses encountered to the first fvar that has a projection @@ -90,21 +97,21 @@ def findOverlappingDataInstances : MetaM Overlaps := do The `Bool` indicates whether the given class key is exactly the type of the associated fvar value. We use this for error reporting. -/ - let mut insts : Std.HashMap Expr (Expr × Bool) := {} + let mut encounteredClasses : Std.HashMap Expr (Expr × Bool) := {} for { fvar := fvar₁, .. } in ← getLocalInstances do unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue let projClasses ← forallTelescope (← inferType fvar₁) fun xs _ ↦ do (← getStructureDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM fun (parentIdx?, expr) => return (parentIdx?, ← mkForallFVars xs expr) for (parentIdxs, cls) in projClasses, idx in 0...* do - if let some (fvar₂, isTypeOfFVar₂) := insts[cls]? then + if let some (fvar₂, isTypeOfFVar₂) := encounteredClasses[cls]? then -- We have encountered a projection with this type already; we should now record an overlap, -- unless it is (or will) be redundant. -- Note that the actions in this branch are allowed to be "slow". let shouldIgnoreCurrent (parentIdx : Nat) (parentClass : Expr) := - -- If a parent further on in `projClasses` will overlap via `fvar₂`, ignore this child. + -- If a parent further on in `projClasses` will overlap via `fvar₂`, ignore this child. -- Note that we can assume `false`, as only the first array element has `true`. - ((idx < parentIdx) && insts[parentClass]?.isEqSome (fvar₂, false)) + (idx < parentIdx && encounteredClasses[parentClass]?.isEqSome (fvar₂, false)) -- If `fvar₁` and `fvar₂` already overlap on a parent, ignore this redundant overlap. || overlaps.containsOverlapOn fvar₁ fvar₂ parentClass -- See if any parent of the current projection, starting with the immediate `parentIdxs`, @@ -112,7 +119,7 @@ def findOverlappingDataInstances : MetaM Overlaps := do unless hasParentP! projClasses shouldIgnoreCurrent parentIdxs do overlaps := overlaps.insert cls (fvar₁, parentIdxs.isEmpty) (fvar₂, isTypeOfFVar₂) else - insts := insts.insert cls (fvar₁, parentIdxs.isEmpty) + encounteredClasses := encounteredClasses.insert cls (fvar₁, parentIdxs.isEmpty) return overlaps /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ From 606dfb5bca2efd4da88c9ccb64d6b8c01c1d6035 Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 17:11:22 -0500 Subject: [PATCH 040/141] chore: handle class inductives, clean up slightly --- .../Tactic/Linter/OverlappingInstances.lean | 42 +++++++++++-------- MathlibTest/OverlappingInstances.lean | 26 ++++++++++++ 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 481a659cc59570..d47150822c1eec 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -21,37 +21,45 @@ public meta section namespace Mathlib.Tactic.OverlappingInstances -/-- Given an instance `e`, compute all data carrying classes that are +/-- +Given an instance `e`, compute all data carrying classes that are the type of `e` itself, or a child class, together with the indices of the parents of each -projection as they appear in this array. -/ -private partial def getStructureDataProjections (e : Expr) (acc : Array (List Nat × Expr) := #[]) +projection as they appear in this array. + +This also records data-carrying non-structure inductive classes in a one-element array. +-/ +private partial def getClassDataProjections (e : Expr) (acc : Array (List Nat × Expr) := #[]) (parentIdx? : Option Nat := none) : - StateRefT (NameMap (Option Nat)) MetaM (Array (List Nat × Expr)) := do + StateRefT (NameMap Nat) MetaM (Array (List Nat × Expr)) := do let eType ← whnf (← inferType e) if ← isProp eType then return acc let .const structName us := eType.getForallBody.getAppFn - | throwError "{e} is not an instance of a structure" - if let some structIdx? := (← get).get? structName then - if let some structIdx := structIdx? then -- `structName` may not have actually been a structure - if let some parentIdx := parentIdx? then - -- `e` has already been recorded, but is being encountered as the child of a new parent at - -- `parentIdx`. Add this parent index to `e`'s original parent indices. - return acc.modify structIdx fun (parents, struct) => (parentIdx :: parents, struct) + | throwError "`{e}` is not an instance of a structure" + -- Check if already recorded + if let some structIdx := (← get).get? structName then + if let some parentIdx := parentIdx? then + -- `e` has already been recorded, but is being encountered as the child of a new parent at + -- `parentIdx`. Add this parent index to `e`'s original parent indices. + return acc.modify structIdx fun (parents, struct) => (parentIdx :: parents, struct) + -- In ordinary usage, where only the first invocation of `getStructureDataProjections` has no + -- parent, this case cannot be reached. return acc + -- Record current class and recurse + let currentIdx := acc.size + let acc := acc.push (parentIdx?.toList, eType) if let some info := getStructureInfo? (← getEnv) structName then - let currentIdx := acc.size -- Record the index at which this structure occurs in `acc`, so we can add to its parents later -- if it is encountered as a projection of something else. modify (·.insert structName currentIdx) - info.parentInfo.foldlM (init := acc.push (parentIdx?.toList, eType)) fun acc info ↦ do + info.parentInfo.foldlM (init := acc) fun acc info ↦ do if ← isInstance info.projFn then let proj := Expr.app (mkAppN (.const info.projFn us) eType.getAppArgs) e - getStructureDataProjections proj acc currentIdx + getClassDataProjections proj acc currentIdx else return acc else - -- Record that we've encountered this constant; however, it is not in the array. - modify (·.insert structName none) + -- This case should only be reached by non-structure inductive classes, and therefore only + -- occur at the root. We still want to record these to warn on duplicate inductive classes. return acc /-- Given an array of projection types paired with indices for their parents, returns `true` if `p` @@ -101,7 +109,7 @@ def findOverlappingDataInstances : MetaM Overlaps := do for { fvar := fvar₁, .. } in ← getLocalInstances do unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue let projClasses ← forallTelescope (← inferType fvar₁) fun xs _ ↦ do - (← getStructureDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM fun (parentIdx?, expr) => + (← getClassDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM fun (parentIdx?, expr) => return (parentIdx?, ← mkForallFVars xs expr) for (parentIdxs, cls) in projClasses, idx in 0...* do if let some (fvar₂, isTypeOfFVar₂) := encounteredClasses[cls]? then diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index fbe80fd336db41..6628d03321f423 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -110,3 +110,29 @@ There should only be a single instance of these data-carrying typeclasses in the private def foo [Add Nat] [Add Nat] : Bool := true end Foo + +section classInductive + +/-! Make sure we warn on duplicate inductive data-carrying inductive classes, even though these do +not have and cannot be structure projections. -/ + +class inductive IndFoo where +| mk₁ (n : Nat) | mk₂ (b : Bool) + +/-- +warning: The declaration `indFoo` has instance hypotheses which provide conflicting versions of the same data. Specifically: + +There are 2 instances of `[IndFoo]`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `indFoo`. +-/ +#guard_msgs in +def indFoo [IndFoo] [IndFoo] : Bool := true + +class inductive IndFooProp : Prop where +| mk₁ (n : Nat) | mk₂ (b : Bool) + +-- Should not warn, these are props +def indFooProp [IndFooProp] [IndFooProp] : Bool := true + +end classInductive From 2b00b9fcf7b61726e84682aa7f549f3e0a209684 Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 17:21:41 -0500 Subject: [PATCH 041/141] chore: a little more documentation --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index d47150822c1eec..e7eb6f2ef92e11 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -95,16 +95,19 @@ Find data-carrying overlaps between the current local instances of the `MetaM` c The resulting `Overlaps` can be assumed to have at least two fvars present for each recorded class. Further, it will only record overlaps at "maximal" nodes in the projection DAG; for example, if -`fvar₁` and `fvar₂` overlap on `cls`, we will not redundantly record their overlap on any -projection `cls.proj`. +`fvar₁` and `fvar₂` overlap on `cls`, the resulting `Overlaps` will not redundantly record their +overlap on any projection `cls.proj`. -/ def findOverlappingDataInstances : MetaM Overlaps := do + /- Records only the overlaps that will eventually be reported. This remains empty iff no + messages should be logged. -/ let mut overlaps : Overlaps := {} /- Associates all (data-carrying) typeclasses encountered to the first fvar that has a projection - into this given typeclass, allowing us to detect when a projection has been seen before. + into the typeclass. We check every projection against this hashmap to detect if it has been seen + before, and add it to the hashmap if not. - The `Bool` indicates whether the given class key is exactly the type of the associated fvar - value. We use this for error reporting. -/ + The keys are classes, and the values are fvars. The `Bool` indicates whether the class key is + exactly the type of the associated fvar. We use this for error reporting. -/ let mut encounteredClasses : Std.HashMap Expr (Expr × Bool) := {} for { fvar := fvar₁, .. } in ← getLocalInstances do unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue From efe34f9b2c78330489a1d9ba881f6ecd3e5e343d Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 18:42:53 -0500 Subject: [PATCH 042/141] chore: make `ofInstanceBinderType` public, resurrect `Mathlib.Lean.Message`, tweak name and docs --- Mathlib/Lean/Message.lean | 13 ++++++++++--- Mathlib/Tactic/Linter/OverlappingInstances.lean | 16 ++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/Mathlib/Lean/Message.lean b/Mathlib/Lean/Message.lean index 8f4ddf531e53d5..dc38a0c1900fab 100644 --- a/Mathlib/Lean/Message.lean +++ b/Mathlib/Lean/Message.lean @@ -1,11 +1,18 @@ /- Copyright (c) 2022 Floris van Doorn. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Floris van Doorn +Authors: Floris van Doorn, Thomas R. Murrills -/ module public import Lean.Message -public import Mathlib.Tactic.Linter.DeprecatedModule -deprecated_module (since := "2025-08-18") +public section + +namespace Lean.MessageData + +/-- Renders an expression `Foo` as an instance binder `[]`, taking care to group and nest +appropriately. This is mostly a synonym for `MessageData.sbracket` for discoverability. -/ +def ofInstanceBinderType (e : Expr) : MessageData := m!"{e}".sbracket + +end Lean.MessageData diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index e7eb6f2ef92e11..5380fbf6f12d2b 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -5,9 +5,10 @@ Authors: Jovan Gerbscheid, Thomas R. Murrills -/ module -public meta import Mathlib.Lean.Elab.InfoTree public meta import Lean.Elab.Command public meta import Mathlib.Lean.ContextInfo +public meta import Mathlib.Lean.Elab.InfoTree +public meta import Mathlib.Lean.Message /-! # A linter to declarations with local instances that have overlapping data @@ -140,11 +141,6 @@ register_option linter.overlappingInstances : Bool := { overlaps and on declaration bodies." } -/-- Surrounds an expression representing the type of an instance with square brackets, taking care -to group and nest appropriately. -/ -private def _root_.Lean.MessageData.ofInstanceType (e : Expr) : MessageData := - m!"{e}".sbracket - /-- Creates a description of the current declaration in messages: "declaration " if the `parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". @@ -169,9 +165,9 @@ def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM Messa let (instsOfOverlap, parentsOfOverlap) := fvars.toList.partitionMap fun (fvar, isFVarType) => if isFVarType then .inl fvar else .inr fvar - let overlapType := m!"`{.ofInstanceType overlap}`" + let overlapType := m!"`{.ofInstanceBinderType overlap}`" let parentTypesOfOverlap := MessageData.andList <|← parentsOfOverlap.mapM fun fvar => - return m!"`{.ofInstanceType <|← inferType fvar}`" + return m!"`{.ofInstanceBinderType <|← inferType fvar}`" let parentTypesOfOverlap := m!"{if let [_, _] := parentsOfOverlap then m!"both " else m!""}{parentTypesOfOverlap}" @@ -209,11 +205,11 @@ def overlappingInstances : Linter where -- Note: we don't break on errors; we want to lint even on partial declarations for t in ← getInfoTrees do for (ctx, info) in t.getDeclBodyInfos do - let some (lctx, localInstances, remainingType?) := info.getLCtxBefore? + let some (lctx, localInstances?, remainingType?) := info.getLCtxBefore? | continue -- TODO: better logging location let outerRef ← getRef - ctx.runMetaMWithMessages lctx (localInstances := localInstances) <| + ctx.runMetaMWithMessages lctx (localInstances := localInstances?) <| withRef outerRef do /- If there's a remaining expected type, then telescope into it in case it contains more instance hypotheses. For now, we don't use the new fvars or return type for anything. -/ From f4d75e618148dba18f5f7abaf5cc60a503a46d76 Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 18:43:47 -0500 Subject: [PATCH 043/141] chore: adjust directory dependency linter --- Mathlib/Tactic/Linter/DirectoryDependency.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/Tactic/Linter/DirectoryDependency.lean b/Mathlib/Tactic/Linter/DirectoryDependency.lean index 28e66d6e914232..dd2ceffab96aaf 100644 --- a/Mathlib/Tactic/Linter/DirectoryDependency.lean +++ b/Mathlib/Tactic/Linter/DirectoryDependency.lean @@ -243,6 +243,7 @@ def allowedImportDirs : NamePrefixRel := .ofArray #[ (`Mathlib.Tactic.Linter, `Mathlib.Tactic.MinImports), (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.ContextInfo), (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Elab.Tactic.Meta), + (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Message), (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Environment), (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Elab.InfoTree), (`Mathlib.Tactic.Linter.TextBased, `Mathlib.Data.Nat.Notation), From 7772c710ebc30220c9c27e249a2a97c6ceba5f4b Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 18:49:36 -0500 Subject: [PATCH 044/141] chore: fix docstring --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 5380fbf6f12d2b..361bedb8fef934 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -142,7 +142,7 @@ register_option linter.overlappingInstances : Bool := { } /-- -Creates a description of the current declaration in messages: "declaration " if the `parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". +Creates a description of the current declaration in messages: "declaration ``" if the `parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". TODO: For now, this does not produce hovers on ``, since the name may clash with the aux decl of the same name in the local context. In the future, we should account for this, and render From 9a487f393abb4f293254263901ebd636cfdfe772 Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 19:24:28 -0500 Subject: [PATCH 045/141] chore: tweak variable names? hmm --- .../Tactic/Linter/OverlappingInstances.lean | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 361bedb8fef934..f679acde730041 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -110,28 +110,28 @@ def findOverlappingDataInstances : MetaM Overlaps := do The keys are classes, and the values are fvars. The `Bool` indicates whether the class key is exactly the type of the associated fvar. We use this for error reporting. -/ let mut encounteredClasses : Std.HashMap Expr (Expr × Bool) := {} - for { fvar := fvar₁, .. } in ← getLocalInstances do - unless (← fvar₁.fvarId!.getBinderInfo).isInstImplicit do continue - let projClasses ← forallTelescope (← inferType fvar₁) fun xs _ ↦ do - (← getClassDataProjections (mkAppN fvar₁ xs) |>.run' {}).mapM fun (parentIdx?, expr) => + for { fvar := fvar, .. } in ← getLocalInstances do + unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue + let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do + (← getClassDataProjections (mkAppN fvar xs) |>.run' {}).mapM fun (parentIdx?, expr) => return (parentIdx?, ← mkForallFVars xs expr) for (parentIdxs, cls) in projClasses, idx in 0...* do - if let some (fvar₂, isTypeOfFVar₂) := encounteredClasses[cls]? then + if let some (fvar₀, clsIsTypeOfFVar₀) := encounteredClasses[cls]? then -- We have encountered a projection with this type already; we should now record an overlap, -- unless it is (or will) be redundant. -- Note that the actions in this branch are allowed to be "slow". let shouldIgnoreCurrent (parentIdx : Nat) (parentClass : Expr) := - -- If a parent further on in `projClasses` will overlap via `fvar₂`, ignore this child. + -- If a parent further on in `projClasses` will overlap via `fvar₀`, ignore this child. -- Note that we can assume `false`, as only the first array element has `true`. - (idx < parentIdx && encounteredClasses[parentClass]?.isEqSome (fvar₂, false)) - -- If `fvar₁` and `fvar₂` already overlap on a parent, ignore this redundant overlap. - || overlaps.containsOverlapOn fvar₁ fvar₂ parentClass + (idx < parentIdx && encounteredClasses[parentClass]?.isEqSome (fvar₀, false)) + -- If `fvar` and `fvar₀` already overlap on a parent, ignore this redundant overlap. + || overlaps.containsOverlapOn fvar fvar₀ parentClass -- See if any parent of the current projection, starting with the immediate `parentIdxs`, -- imply it is redundant. unless hasParentP! projClasses shouldIgnoreCurrent parentIdxs do - overlaps := overlaps.insert cls (fvar₁, parentIdxs.isEmpty) (fvar₂, isTypeOfFVar₂) + overlaps := overlaps.insert cls (fvar, parentIdxs.isEmpty) (fvar₀, clsIsTypeOfFVar₀) else - encounteredClasses := encounteredClasses.insert cls (fvar₁, parentIdxs.isEmpty) + encounteredClasses := encounteredClasses.insert cls (fvar, parentIdxs.isEmpty) return overlaps /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ From 9dfbdd0a4162b00630a331fa6c140590d2ded70d Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 19:43:56 -0500 Subject: [PATCH 046/141] chore: us `Array`s in `Overlap` implementation --- .../Tactic/Linter/OverlappingInstances.lean | 26 +++++++++++-------- MathlibTest/OverlappingInstances.lean | 4 +-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index f679acde730041..6fbb509f820b42 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -75,21 +75,22 @@ private partial def hasParentP! (projections : Array (List Nat × Expr)) (p : Na /-- Stores the local instance overlaps per class. The keys are the class, and the values are local instances which have the class as a projection. The `Bool` value of each entry indicates whether -its type is exactly the key class. We use an `ExprMap Bool` here instead of e.g. an -`Array (Expr × Bool)` to ensure that each local instance is recorded only once. There may be -assumed to be at least two local instances per class. -/ -abbrev Overlaps := ExprMap (ExprMap Bool) +its type is exactly the key class. The code constructing values of this class is responsible for +ensuring that (1) every `Array` value contains at least two elements (2) no element of the array +appears twice. -/ +abbrev Overlaps := ExprMap (Array (Expr × Bool)) -/-- Inserts an overlap into `Overlaps`. -/ -def Overlaps.insert (cls : Expr) (fvar₁ fvar₂ : Expr × Bool) (overlaps : Overlaps) : Overlaps := - overlaps.alter cls fun map? => - map?.getD ∅ |>.insert fvar₁.1 fvar₁.2 |>.insert fvar₂.1 fvar₂.2 +/-- Inserts a single `fvar` into the set of overlaps for `cls`. -/ +def Overlaps.pushAt (cls : Expr) (fvar : Expr × Bool) (overlaps : Overlaps) : Overlaps := + overlaps.alter cls fun + | none => some #[fvar] + | some o => o.push fvar /-- Returns `true` iff `fvar₁` and `fvar₂` overlap on the `cls` projection typeclass. -/ def Overlaps.containsOverlapOn (fvar₁ fvar₂ : Expr) (cls : Expr) (overlaps : Overlaps) : Bool := match overlaps[cls]? with | none => false - | some overlap => overlap.contains fvar₁ && overlap.contains fvar₂ + | some overlap => overlap.any (·.1 == fvar₁) && overlap.any (·.1 == fvar₂) /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. @@ -125,11 +126,14 @@ def findOverlappingDataInstances : MetaM Overlaps := do -- Note that we can assume `false`, as only the first array element has `true`. (idx < parentIdx && encounteredClasses[parentClass]?.isEqSome (fvar₀, false)) -- If `fvar` and `fvar₀` already overlap on a parent, ignore this redundant overlap. - || overlaps.containsOverlapOn fvar fvar₀ parentClass + || overlaps.containsOverlapOn fvar₀ fvar parentClass -- See if any parent of the current projection, starting with the immediate `parentIdxs`, -- imply it is redundant. unless hasParentP! projClasses shouldIgnoreCurrent parentIdxs do - overlaps := overlaps.insert cls (fvar, parentIdxs.isEmpty) (fvar₀, clsIsTypeOfFVar₀) + -- If no overlap exists yet for `cls`, start by inserting `fvar₀`. + unless overlaps.contains cls do + overlaps := overlaps.pushAt cls (fvar₀, clsIsTypeOfFVar₀) + overlaps := overlaps.pushAt cls (fvar, parentIdxs.isEmpty) else encounteredClasses := encounteredClasses.insert cls (fvar, parentIdxs.isEmpty) return overlaps diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 6628d03321f423..eab688f1a15f47 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -43,7 +43,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- warning: The declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: -`[Bar Nat]` is provided by both `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. +`[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaq Nat]`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₁`. -/ @@ -55,7 +55,7 @@ def foo₁ [FooBarBaz Nat] [FooBarBaq Nat] : Bool := by warning: The declaration `foo₂` has instance hypotheses which provide conflicting versions of the same data. Specifically: • There are 2 instances of `[FooBarBaz Nat]`. -• `[Bar Nat]` is provided by both `[FooBarBaq Nat]` and `[FooBarBaz Nat]`. +• `[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaq Nat]`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₂`. -/ From 05cc29e5081629c1460e3c74bfa057499bf91954 Mon Sep 17 00:00:00 2001 From: thorimur Date: Thu, 5 Feb 2026 19:49:14 -0500 Subject: [PATCH 047/141] chore: clean up --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 6fbb509f820b42..d17e50525b7be1 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -88,9 +88,7 @@ def Overlaps.pushAt (cls : Expr) (fvar : Expr × Bool) (overlaps : Overlaps) : O /-- Returns `true` iff `fvar₁` and `fvar₂` overlap on the `cls` projection typeclass. -/ def Overlaps.containsOverlapOn (fvar₁ fvar₂ : Expr) (cls : Expr) (overlaps : Overlaps) : Bool := - match overlaps[cls]? with - | none => false - | some overlap => overlap.any (·.1 == fvar₁) && overlap.any (·.1 == fvar₂) + overlaps[cls]?.any fun overlap => overlap.any (·.1 == fvar₁) && overlap.any (·.1 == fvar₂) /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. From 33dc4f5adca9a432ae508461769f811aef894415 Mon Sep 17 00:00:00 2001 From: thorimur Date: Fri, 6 Feb 2026 19:19:24 -0500 Subject: [PATCH 048/141] chore: condition refactor, first pass --- .../Tactic/Linter/OverlappingInstances.lean | 61 +++++++++++-------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index d17e50525b7be1..dacf27625e2853 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -63,16 +63,6 @@ private partial def getClassDataProjections (e : Expr) (acc : Array (List Nat × -- occur at the root. We still want to record these to warn on duplicate inductive classes. return acc -/-- Given an array of projection types paired with indices for their parents, returns `true` if `p` -is true for the types at any of the starting indices or their transitive parents. -/ -private partial def hasParentP! (projections : Array (List Nat × Expr)) (p : Nat → Expr → Bool) - (startingIdxs : List Nat) : Bool := - match startingIdxs with - | [] => false - | idx :: idxs => - let (parentIdxs, expr) := projections[idx]! - p idx expr || hasParentP! projections p parentIdxs || hasParentP! projections p idxs - /-- Stores the local instance overlaps per class. The keys are the class, and the values are local instances which have the class as a projection. The `Bool` value of each entry indicates whether its type is exactly the key class. The code constructing values of this class is responsible for @@ -86,9 +76,9 @@ def Overlaps.pushAt (cls : Expr) (fvar : Expr × Bool) (overlaps : Overlaps) : O | none => some #[fvar] | some o => o.push fvar -/-- Returns `true` iff `fvar₁` and `fvar₂` overlap on the `cls` projection typeclass. -/ -def Overlaps.containsOverlapOn (fvar₁ fvar₂ : Expr) (cls : Expr) (overlaps : Overlaps) : Bool := - overlaps[cls]?.any fun overlap => overlap.any (·.1 == fvar₁) && overlap.any (·.1 == fvar₂) +/-- Returns `true` iff `fvar` is among the overlaps recorded for `cls`. -/ +def Overlaps.containsAt (cls : Expr) (fvar : Expr) (overlaps : Overlaps) : Bool := + overlaps[cls]?.any fun overlap => overlap.any (·.1 == fvar) /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. @@ -98,7 +88,7 @@ Further, it will only record overlaps at "maximal" nodes in the projection DAG; `fvar₁` and `fvar₂` overlap on `cls`, the resulting `Overlaps` will not redundantly record their overlap on any projection `cls.proj`. -/ -def findOverlappingDataInstances : MetaM Overlaps := do +partial def findOverlappingDataInstances : MetaM Overlaps := do /- Records only the overlaps that will eventually be reported. This remains empty iff no messages should be logged. -/ let mut overlaps : Overlaps := {} @@ -106,33 +96,52 @@ def findOverlappingDataInstances : MetaM Overlaps := do into the typeclass. We check every projection against this hashmap to detect if it has been seen before, and add it to the hashmap if not. - The keys are classes, and the values are fvars. The `Bool` indicates whether the class key is - exactly the type of the associated fvar. We use this for error reporting. -/ + The keys are classes, and the values are the representative fvars. The `Bool` indicates whether + the class key is exactly the type of the associated fvar. We use this for error reporting. -/ let mut encounteredClasses : Std.HashMap Expr (Expr × Bool) := {} for { fvar := fvar, .. } in ← getLocalInstances do unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do (← getClassDataProjections (mkAppN fvar xs) |>.run' {}).mapM fun (parentIdx?, expr) => return (parentIdx?, ← mkForallFVars xs expr) - for (parentIdxs, cls) in projClasses, idx in 0...* do + for (parentIdxs, cls) in projClasses do if let some (fvar₀, clsIsTypeOfFVar₀) := encounteredClasses[cls]? then -- We have encountered a projection with this type already; we should now record an overlap, -- unless it is (or will) be redundant. -- Note that the actions in this branch are allowed to be "slow". - let shouldIgnoreCurrent (parentIdx : Nat) (parentClass : Expr) := - -- If a parent further on in `projClasses` will overlap via `fvar₀`, ignore this child. - -- Note that we can assume `false`, as only the first array element has `true`. - (idx < parentIdx && encounteredClasses[parentClass]?.isEqSome (fvar₀, false)) - -- If `fvar` and `fvar₀` already overlap on a parent, ignore this redundant overlap. - || overlaps.containsOverlapOn fvar₀ fvar parentClass - -- See if any parent of the current projection, starting with the immediate `parentIdxs`, - -- imply it is redundant. - unless hasParentP! projClasses shouldIgnoreCurrent parentIdxs do + + /- Whether `fvar₀` yields the presciently-named class `parentClass` as a projection. This + occurs iff either + - `fvar₀` represents `cls` in `encounteredClasses` + - or `fvar₀` is in an overlap on `cls`. + Note: since in this context of use `parentClass` may be further on in `fvar`'s + `projClasses` (and so an overlap between `fvar` and `fvar₀` may not have been recorded yet) + we must check `encounteredClasses`. -/ + let isProjectionOfFVar₀ (parentClass : Expr) := + encounteredClasses[parentClass]?.any (·.1 == fvar₀) + /- Technical note: this does not test that `fvar` is in the *same* overlap as `fvar₀`. + However, if `fvar₀` is in *an* overlap on some parent of `fvar`, we trust it at least + shares an overlap with `fvar` on some (possibly distinct) parent of `fvar`. -/ + || overlaps.containsAt parentClass fvar₀ + let rec + /-- Tests if any (strict) parents are yielded by `fvar₀`. If so, then some parent of + the current `cls` is yielded by both `fvar` and `fvar₀`, and we should (only) record + the overlap on the parent class; the current overlap is redundant. -/ + overlapsOnParent (parentIdxs : List Nat) : Bool := + parentIdxs.any fun parentIdx => + let (nextParentIdxs, parentClass) := projClasses[parentIdx]! + isProjectionOfFVar₀ parentClass || overlapsOnParent nextParentIdxs + + -- If `fvar` overlaps with `fvar₀` on a parent, don't record an overlap--it is redundant. + unless overlapsOnParent parentIdxs do -- If no overlap exists yet for `cls`, start by inserting `fvar₀`. + -- Otherwise, we assume `fvar₀`, being the representative of `cls`, was already + -- inserted into the overlap when it was first populated. unless overlaps.contains cls do overlaps := overlaps.pushAt cls (fvar₀, clsIsTypeOfFVar₀) overlaps := overlaps.pushAt cls (fvar, parentIdxs.isEmpty) else + -- `cls` has no representative yet, so insert the current fvar as the representative. encounteredClasses := encounteredClasses.insert cls (fvar, parentIdxs.isEmpty) return overlaps From 259ef14a96b6288ea9fe9009c6a5a13885922374 Mon Sep 17 00:00:00 2001 From: thorimur Date: Fri, 6 Feb 2026 19:22:03 -0500 Subject: [PATCH 049/141] chore: wording --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index dacf27625e2853..2447f11cd5f40d 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -121,7 +121,7 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do encounteredClasses[parentClass]?.any (·.1 == fvar₀) /- Technical note: this does not test that `fvar` is in the *same* overlap as `fvar₀`. However, if `fvar₀` is in *an* overlap on some parent of `fvar`, we trust it at least - shares an overlap with `fvar` on some (possibly distinct) parent of `fvar`. -/ + shares an overlap with `fvar` on some (possibly distinct) parent of `cls`. -/ || overlaps.containsAt parentClass fvar₀ let rec /-- Tests if any (strict) parents are yielded by `fvar₀`. If so, then some parent of From 9b5f661c4f7bc95a0a69c532e1fe060d04b6b17f Mon Sep 17 00:00:00 2001 From: thorimur Date: Fri, 6 Feb 2026 19:25:07 -0500 Subject: [PATCH 050/141] chore: wording --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 2447f11cd5f40d..4d587a83fd067c 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -136,7 +136,7 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do unless overlapsOnParent parentIdxs do -- If no overlap exists yet for `cls`, start by inserting `fvar₀`. -- Otherwise, we assume `fvar₀`, being the representative of `cls`, was already - -- inserted into the overlap when it was first populated. + -- inserted into the overlap when the overlap was first populated. unless overlaps.contains cls do overlaps := overlaps.pushAt cls (fvar₀, clsIsTypeOfFVar₀) overlaps := overlaps.pushAt cls (fvar, parentIdxs.isEmpty) From 8ef752655f6425193f65d5b1c69ee56bd9654b81 Mon Sep 17 00:00:00 2001 From: thorimur Date: Fri, 6 Feb 2026 19:32:02 -0500 Subject: [PATCH 051/141] chore: consider alternate factoring --- .../Tactic/Linter/OverlappingInstances.lean | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 4d587a83fd067c..f045e630cc5cd3 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -63,6 +63,14 @@ private partial def getClassDataProjections (e : Expr) (acc : Array (List Nat × -- occur at the root. We still want to record these to warn on duplicate inductive classes. return acc +/-- Given an array of projection types paired with the locations of their parents, returns `true` +if `p` is true for the types at any of the starting indices or their transitive parents. -/ +private partial def hasAnyParentWhich (p : Expr → Bool) + (projections : Array (List Nat × Expr)) (startingIdxs : List Nat) : Bool := + startingIdxs.any fun idx => + let (parentIdxs, cls) := projections[idx]! + p cls || hasAnyParentWhich p projections parentIdxs + /-- Stores the local instance overlaps per class. The keys are the class, and the values are local instances which have the class as a projection. The `Bool` value of each entry indicates whether its type is exactly the key class. The code constructing values of this class is responsible for @@ -123,17 +131,12 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do However, if `fvar₀` is in *an* overlap on some parent of `fvar`, we trust it at least shares an overlap with `fvar` on some (possibly distinct) parent of `cls`. -/ || overlaps.containsAt parentClass fvar₀ - let rec - /-- Tests if any (strict) parents are yielded by `fvar₀`. If so, then some parent of - the current `cls` is yielded by both `fvar` and `fvar₀`, and we should (only) record - the overlap on the parent class; the current overlap is redundant. -/ - overlapsOnParent (parentIdxs : List Nat) : Bool := - parentIdxs.any fun parentIdx => - let (nextParentIdxs, parentClass) := projClasses[parentIdx]! - isProjectionOfFVar₀ parentClass || overlapsOnParent nextParentIdxs - - -- If `fvar` overlaps with `fvar₀` on a parent, don't record an overlap--it is redundant. - unless overlapsOnParent parentIdxs do + + /- Tests if any (strict) parents of `cls` are yielded by `fvar₀` as a projection. If so, + then `fvar` and `fvar₀` overlap (or will overlap) on some parent of the current `cls`. + We should (only) record overlaps on the maximal parent class(es); the current overlap is + therefore redundant, and we skip it. -/ + unless hasAnyParentWhich isProjectionOfFVar₀ projClasses parentIdxs do -- If no overlap exists yet for `cls`, start by inserting `fvar₀`. -- Otherwise, we assume `fvar₀`, being the representative of `cls`, was already -- inserted into the overlap when the overlap was first populated. From 8531cbcb32d6ccb962cad1b60e1197872d3bf862 Mon Sep 17 00:00:00 2001 From: thorimur Date: Fri, 6 Feb 2026 19:33:33 -0500 Subject: [PATCH 052/141] chore: too much documentation? not relevant anymore? --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index f045e630cc5cd3..4ffb407ea759e4 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -121,15 +121,9 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do /- Whether `fvar₀` yields the presciently-named class `parentClass` as a projection. This occurs iff either - `fvar₀` represents `cls` in `encounteredClasses` - - or `fvar₀` is in an overlap on `cls`. - Note: since in this context of use `parentClass` may be further on in `fvar`'s - `projClasses` (and so an overlap between `fvar` and `fvar₀` may not have been recorded yet) - we must check `encounteredClasses`. -/ + - or `fvar₀` is in an overlap on `cls`. -/ let isProjectionOfFVar₀ (parentClass : Expr) := encounteredClasses[parentClass]?.any (·.1 == fvar₀) - /- Technical note: this does not test that `fvar` is in the *same* overlap as `fvar₀`. - However, if `fvar₀` is in *an* overlap on some parent of `fvar`, we trust it at least - shares an overlap with `fvar` on some (possibly distinct) parent of `cls`. -/ || overlaps.containsAt parentClass fvar₀ /- Tests if any (strict) parents of `cls` are yielded by `fvar₀` as a projection. If so, From d6a50c1e16a40ac48acc6c0a6f5708f797531ae8 Mon Sep 17 00:00:00 2001 From: thorimur Date: Fri, 6 Feb 2026 19:33:54 -0500 Subject: [PATCH 053/141] chore: wording --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 4ffb407ea759e4..bdcc160d988400 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -115,7 +115,7 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do for (parentIdxs, cls) in projClasses do if let some (fvar₀, clsIsTypeOfFVar₀) := encounteredClasses[cls]? then -- We have encountered a projection with this type already; we should now record an overlap, - -- unless it is (or will) be redundant. + -- unless it is (or will be) redundant. -- Note that the actions in this branch are allowed to be "slow". /- Whether `fvar₀` yields the presciently-named class `parentClass` as a projection. This From eb6e433db9aadc944ac5517fe4211ecd041d89f7 Mon Sep 17 00:00:00 2001 From: thorimur Date: Fri, 6 Feb 2026 20:07:24 -0500 Subject: [PATCH 054/141] chore: wording --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index bdcc160d988400..5353c78b158123 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -120,8 +120,8 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do /- Whether `fvar₀` yields the presciently-named class `parentClass` as a projection. This occurs iff either - - `fvar₀` represents `cls` in `encounteredClasses` - - or `fvar₀` is in an overlap on `cls`. -/ + - `fvar₀` represents `parentClass` in `encounteredClasses` + - or `fvar₀` is in an overlap on `parentClass`. -/ let isProjectionOfFVar₀ (parentClass : Expr) := encounteredClasses[parentClass]?.any (·.1 == fvar₀) || overlaps.containsAt parentClass fvar₀ From bd24af7ef3ecb4caf595cf7c308eede1e09eb763 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 7 Feb 2026 14:40:25 -0500 Subject: [PATCH 055/141] chore: import header in `Mathlib.Lean.Message` --- Mathlib/Lean/Message.lean | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mathlib/Lean/Message.lean b/Mathlib/Lean/Message.lean index dc38a0c1900fab..a2d402fadbe58b 100644 --- a/Mathlib/Lean/Message.lean +++ b/Mathlib/Lean/Message.lean @@ -6,6 +6,9 @@ Authors: Floris van Doorn, Thomas R. Murrills module public import Lean.Message +-- Import this linter explicitly to ensure that +-- this file has a valid copyright header and module docstring. +public import Mathlib.Tactic.Linter.Header public section From cb9577a6fecc46200cd07ef187d8f94ae8e05438 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 7 Feb 2026 14:41:26 -0500 Subject: [PATCH 056/141] chore: module docstring for `Mathlib.Lean.Message` --- Mathlib/Lean/Message.lean | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mathlib/Lean/Message.lean b/Mathlib/Lean/Message.lean index a2d402fadbe58b..d20e747bcd58d8 100644 --- a/Mathlib/Lean/Message.lean +++ b/Mathlib/Lean/Message.lean @@ -10,6 +10,10 @@ public import Lean.Message -- this file has a valid copyright header and module docstring. public import Mathlib.Tactic.Linter.Header +/-! +# Additional utilities for `MessageData` +-/ + public section namespace Lean.MessageData From 3be4374053b8caae7068ed921741d2446c318a4a Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 7 Feb 2026 15:42:56 -0500 Subject: [PATCH 057/141] chore: namespace under `Linter` instead of `Tactic` --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 5353c78b158123..99f4f892caddb5 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -20,7 +20,7 @@ open Lean Meta Elab Command public meta section -namespace Mathlib.Tactic.OverlappingInstances +namespace Mathlib.Linter.OverlappingInstances /-- Given an instance `e`, compute all data carrying classes that are @@ -229,4 +229,4 @@ def overlappingInstances : Linter where initialize addLinter overlappingInstances -end Mathlib.Tactic.OverlappingInstances +end Mathlib.Linter.OverlappingInstances From 976a8eab638cd0203fb2386b962a1b360d5c6670 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:46:43 -0500 Subject: [PATCH 058/141] fix: instantiateMVars --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- MathlibTest/OverlappingInstances.lean | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 99f4f892caddb5..44494448784f3f 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -111,7 +111,7 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do (← getClassDataProjections (mkAppN fvar xs) |>.run' {}).mapM fun (parentIdx?, expr) => - return (parentIdx?, ← mkForallFVars xs expr) + return (parentIdx?, ← instantiateMVars <|← mkForallFVars xs expr) for (parentIdxs, cls) in projClasses do if let some (fvar₀, clsIsTypeOfFVar₀) := encounteredClasses[cls]? then -- We have encountered a projection with this type already; we should now record an overlap, diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index eab688f1a15f47..e53341164efadc 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -1,6 +1,7 @@ module import Mathlib.Tactic.Linter.OverlappingInstances +import Mathlib.Tactic.TypeStar set_option linter.overlappingInstances true @@ -136,3 +137,11 @@ class inductive IndFooProp : Prop where def indFooProp [IndFooProp] [IndFooProp] : Bool := true end classInductive + +section instantiateMVars + +variable {α : Type*} [Repr α] + +def needsInstantiateMVars [Repr α] : Bool := true + +end instantiateMVars From 95098e71f0d810343282ced8b01cf72e34d1b362 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:47:41 -0500 Subject: [PATCH 059/141] chore: actually guard the messages --- MathlibTest/OverlappingInstances.lean | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index e53341164efadc..19dfbe9d7a2990 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -142,6 +142,14 @@ section instantiateMVars variable {α : Type*} [Repr α] +/-- +warning: The declaration `needsInstantiateMVars` has instance hypotheses which provide conflicting versions of the same data. Specifically: + +There are 2 instances of `[Repr α]`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `needsInstantiateMVars`. +-/ +#guard_msgs in def needsInstantiateMVars [Repr α] : Bool := true end instantiateMVars From 38d06a84243fafdc8197056fb352a1b4ff0029dc Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 7 Feb 2026 20:13:34 -0500 Subject: [PATCH 060/141] fix: better place for instantiateMVars? --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 44494448784f3f..fe50f01241cecb 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -32,7 +32,7 @@ This also records data-carrying non-structure inductive classes in a one-element private partial def getClassDataProjections (e : Expr) (acc : Array (List Nat × Expr) := #[]) (parentIdx? : Option Nat := none) : StateRefT (NameMap Nat) MetaM (Array (List Nat × Expr)) := do - let eType ← whnf (← inferType e) + let eType ← whnf <|← instantiateMVars <|← inferType e if ← isProp eType then return acc let .const structName us := eType.getForallBody.getAppFn | throwError "`{e}` is not an instance of a structure" @@ -111,7 +111,7 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do (← getClassDataProjections (mkAppN fvar xs) |>.run' {}).mapM fun (parentIdx?, expr) => - return (parentIdx?, ← instantiateMVars <|← mkForallFVars xs expr) + return (parentIdx?, ← mkForallFVars xs expr) for (parentIdxs, cls) in projClasses do if let some (fvar₀, clsIsTypeOfFVar₀) := encounteredClasses[cls]? then -- We have encountered a projection with this type already; we should now record an overlap, From cc49f64f96883c2958f78bd3809f2ab19ba0097e Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:28:07 -0500 Subject: [PATCH 061/141] chore: refactor to keep classes in order? --- .../Tactic/Linter/OverlappingInstances.lean | 41 ++++++++++--------- MathlibTest/OverlappingInstances.lean | 2 +- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index fe50f01241cecb..21529fcf77da6f 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -71,22 +71,30 @@ private partial def hasAnyParentWhich (p : Expr → Bool) let (parentIdxs, cls) := projections[idx]! p cls || hasAnyParentWhich p projections parentIdxs -/-- Stores the local instance overlaps per class. The keys are the class, and the values are local -instances which have the class as a projection. The `Bool` value of each entry indicates whether -its type is exactly the key class. The code constructing values of this class is responsible for -ensuring that (1) every `Array` value contains at least two elements (2) no element of the array -appears twice. -/ -abbrev Overlaps := ExprMap (Array (Expr × Bool)) - -/-- Inserts a single `fvar` into the set of overlaps for `cls`. -/ -def Overlaps.pushAt (cls : Expr) (fvar : Expr × Bool) (overlaps : Overlaps) : Overlaps := - overlaps.alter cls fun - | none => some #[fvar] - | some o => o.push fvar +/-- Stores the local instance overlaps per class. The "keys" are the class, and the values are local +instances which have the class as a projection. The `Bool` in the value of each entry indicates +whether its type is exactly the key class. + +The code constructing values of this class is responsible for ensuring that (1) every `Array` value +contains at least two elements (2) no element of the array appears twice. + +We use an `Array` instead of a hashmap in order to record the overlaps in the order the classes +appear. -/ +abbrev Overlaps := Array <| Expr × Array (Expr × Bool) + +/-- Inserts `fvar₁` into the overlap for `cls` with `fvar₀`, assuming `fvar₀` is the representative +of the class. Since this is the only way we insert fvars and the representatives do not change, we +assume the representative `fvar₀` has already been inserted if the overlap exists, and do not +re-insert it. -/ +def Overlaps.pushOverlap (fvar₀ : Expr × Bool) (cls : Expr) (fvar₁ : Expr × Bool) + (overlaps : Overlaps) : Overlaps := + match overlaps.findIdx? (·.1 == cls) with + | none => overlaps.push (cls, #[fvar₀, fvar₁]) + | some idx => overlaps.modify idx fun (cls, overlap) => (cls, overlap.push fvar₁) /-- Returns `true` iff `fvar` is among the overlaps recorded for `cls`. -/ def Overlaps.containsAt (cls : Expr) (fvar : Expr) (overlaps : Overlaps) : Bool := - overlaps[cls]?.any fun overlap => overlap.any (·.1 == fvar) + overlaps.any fun (cls', overlap) => cls == cls' && overlap.any (·.1 == fvar) /-- Find data-carrying overlaps between the current local instances of the `MetaM` context. @@ -131,12 +139,7 @@ partial def findOverlappingDataInstances : MetaM Overlaps := do We should (only) record overlaps on the maximal parent class(es); the current overlap is therefore redundant, and we skip it. -/ unless hasAnyParentWhich isProjectionOfFVar₀ projClasses parentIdxs do - -- If no overlap exists yet for `cls`, start by inserting `fvar₀`. - -- Otherwise, we assume `fvar₀`, being the representative of `cls`, was already - -- inserted into the overlap when the overlap was first populated. - unless overlaps.contains cls do - overlaps := overlaps.pushAt cls (fvar₀, clsIsTypeOfFVar₀) - overlaps := overlaps.pushAt cls (fvar, parentIdxs.isEmpty) + overlaps := overlaps.pushOverlap (fvar₀, clsIsTypeOfFVar₀) cls (fvar, parentIdxs.isEmpty) else -- `cls` has no representative yet, so insert the current fvar as the representative. encounteredClasses := encounteredClasses.insert cls (fvar, parentIdxs.isEmpty) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 19dfbe9d7a2990..cf354e61e13fb4 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -88,8 +88,8 @@ def foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : Bool := true /-- warning: The declaration `foo₅` has instance hypotheses which provide conflicting versions of the same data. Specifically: -• `[Baz Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. • `[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. +• `[Baz Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₅`. -/ From fb03eb2faace6a4fa14bc7fdcb4e505a9a15ab1a Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:02:05 -0500 Subject: [PATCH 062/141] docs: module docstring --- .../Tactic/Linter/OverlappingInstances.lean | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 21529fcf77da6f..94cd4d54771f0a 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -11,9 +11,40 @@ public meta import Mathlib.Lean.Elab.InfoTree public meta import Mathlib.Lean.Message /-! -# A linter to declarations with local instances that have overlapping data +# A linter for declarations with local instances that have overlapping data + +If the same data can be obtained from two different instances in the local context, we risk having +non-defeq versions of that data. This situation, both for declarations and more broadly, is known +as an "instance diamond". This linter warns against declarations whose local contexts include +multiple versions of the same data. + +This is a syntax linter intended for linting declaration bodies that appear in source. It does not +lint autogenerated declarations or declarations from imported modules. + +Note that since all proofs of a given proposition are definitionally equal, multiple different ways +of obtaining instances of `Prop` classes pose no issue. Hence, this linter only warns against +data-carrying instance projections. + +Note that since this linter also warns against the trivial case of the same data-carrying instance +appearing twice, it warns against explicit local instance hypotheses which shadow `variable`s. +These may not influence the resulting type of the declaration, since Lean ignores unused instances, +but they are still duplicated in the local context while editing the body. + +## TODO + +- The logging location for this linter could be improved. +- Currently it is possible to obtain a message which includes something of the following form: + ``` + • There are 2 instances of `[NonUnitalSemiring R]`. + • `[InvolutiveStar R]` is provided by both `[StarRing R]` and `[StarRing R]`. + ``` + This occurs because each of the two `StarRing`s relies on one of the two different + `NonUnitalSemiring` instances in the context, making them distinct (despite pretty-printing the + same way). However, their projection to `InvolutiveStar` no longer depends on this instance, and + thus coincides. The messages in this scenario could be improved. +- We could add hovers on the declaration name in messages. This is made tricky by the fact that it + conflicts with the auxdecl of the same name. -We want to avoid this because this lead to instance diamonds -/ open Lean Meta Elab Command From cdcc316267221b12271bc56c0f53d0feb94176d4 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:14:51 -0500 Subject: [PATCH 063/141] chore: cheap better logging location --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 14 +++++++++++++- MathlibTest/OverlappingInstances.lean | 9 +++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 94cd4d54771f0a..f3ca64691ff03a 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -232,6 +232,18 @@ def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM Messa at a time. Consider choosing different instance hypotheses for the {declDescr}." addMessageContextFull msg +/-- This is a cheap way of getting a usefully better logging location: descend into `in`, and +remove decl modifiers. This is still not ideal (e.g. it does not take care of `mutual`, and extends +across the whole command), but prevents us from logging on e.g. the top of the docstring in most +cases. -/ +private partial def stripInAndModifiers (cmd : Syntax) : Syntax := + if cmd.isOfKind ``Parser.Command.in then + stripInAndModifiers cmd[2] + else if cmd[0].isOfKind ``Parser.Command.declModifiers then + cmd[1] + else + cmd + open Linter in /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. @@ -250,7 +262,7 @@ def overlappingInstances : Linter where let some (lctx, localInstances?, remainingType?) := info.getLCtxBefore? | continue -- TODO: better logging location - let outerRef ← getRef + let outerRef := stripInAndModifiers cmd ctx.runMetaMWithMessages lctx (localInstances := localInstances?) <| withRef outerRef do /- If there's a remaining expected type, then telescope into it in case it contains more diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index cf354e61e13fb4..6b2c43e8c309d5 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -7,6 +7,8 @@ set_option linter.overlappingInstances true namespace Lean +public section + class SubBar (α : Type) where a' : α @@ -42,14 +44,17 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- +@ +3:17...+4:12 warning: The declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaq Nat]`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₁`. -/ -#guard_msgs in -def foo₁ [FooBarBaz Nat] [FooBarBaq Nat] : Bool := by +#guard_msgs (positions := true) in +set_option linter.overlappingInstances true in +/-- A docstring! -/ +@[expose] public def foo₁ [FooBarBaz Nat] [FooBarBaq Nat] : Bool := by exact true /-- From c60ff1a7bd5b503fb515f28e149ee458cdb2e452 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:27:48 -0500 Subject: [PATCH 064/141] chore: disable noninteractively --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index f3ca64691ff03a..d7cb54fb592297 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -21,6 +21,9 @@ multiple versions of the same data. This is a syntax linter intended for linting declaration bodies that appear in source. It does not lint autogenerated declarations or declarations from imported modules. +For performance reasons, this linter **only** runs interactively in the language server. It will +not run on the command line during a typical `lake build`. + Note that since all proofs of a given proposition are definitionally equal, multiple different ways of obtaining instances of `Prop` classes pose no issue. Hence, this linter only warns against data-carrying instance projections. @@ -32,6 +35,7 @@ but they are still duplicated in the local context while editing the body. ## TODO +- Improve performance. Currently running this linter in CI is prohibitively expensive. - The logging location for this linter could be improved. - Currently it is possible to obtain a message which includes something of the following form: ``` @@ -183,6 +187,14 @@ register_option linter.overlappingInstances : Bool := { overlaps and on declaration bodies." } +/-- Whether the overlapping instances linter runs only in the language server (the default). -/ +register_option linter.overlappingInstances.onlyInServer : Bool := { + defValue := true + descr := "runs the overlapping instance linter only when `Elab.inServer` + is `true`. Setting this to `false` will also run the linter during `lake build`; this is + currently discouraged for performance reasons." +} + /-- Creates a description of the current declaration in messages: "declaration ``" if the `parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". @@ -254,6 +266,10 @@ def overlappingInstances : Linter where run cmd := do unless getLinterValue linter.overlappingInstances (← getLinterOptions) do return + -- Note that we do not consider this a linter option to avoid turning it on with `linter.all`. + if ← linter.overlappingInstances.onlyInServer.getM then + unless ← Elab.inServer.getM do + return /- TODO: use `withSetOptionIn` when either it's fixed via the open lean PR or `unusedFintypeInType` lands with a workaround -/ -- Note: we don't break on errors; we want to lint even on partial declarations From f268642548bdc09df61642087d866110c272b3ef Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:34:10 -0500 Subject: [PATCH 065/141] chore: temporarily inline `withSetBoolOptionIn` --- .../Tactic/Linter/OverlappingInstances.lean | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index d7cb54fb592297..0528450c37fdfb 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -256,6 +256,27 @@ private partial def stripInAndModifiers (cmd : Syntax) : Syntax := else cmd +/-- `withSetOptionIn` currently breaks infotree searches, so we simply set `Bool` options +until this is fixed in [lean4#11313](https://github.com/leanprover/lean4/pull/11313). + +This declaration will be removed in favor of `withSetOptionIn` when lean4#11313 lands. -/ +private partial def withSetBoolOptionIn (x : CommandElab) : CommandElab + | `(command| set_option $opt:ident $val in $cmd:command) => do + match val.raw with + | Syntax.atom _ "true" => + withBoolOption opt.getId true <| withSetBoolOptionIn x cmd + | Syntax.atom _ "false" => + withBoolOption opt.getId false <| withSetBoolOptionIn x cmd + | _ => withSetBoolOptionIn x cmd + | `(command| $_:command in $cmd:command) => withSetBoolOptionIn x cmd + | stx => x stx +where + /-- Set a `Bool` option in `CommandElabM`. Ideally, `CommandElabM` would have a + `MonadWithOptions` instance to this effect. -/ + withBoolOption (n : Name) (val : Bool) (k : CommandElabM Unit) : CommandElabM Unit := do + let opts := (← getOptions).setBool n val + Command.withScope (fun scope => { scope with opts }) k + open Linter in /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. @@ -263,15 +284,13 @@ Lints against data-carrying overlaps between instances in the local contexts of Note: currently does not respect `set_option`. -/ def overlappingInstances : Linter where - run cmd := do + run := withSetBoolOptionIn fun cmd => do unless getLinterValue linter.overlappingInstances (← getLinterOptions) do return -- Note that we do not consider this a linter option to avoid turning it on with `linter.all`. if ← linter.overlappingInstances.onlyInServer.getM then unless ← Elab.inServer.getM do return - /- TODO: use `withSetOptionIn` when either it's fixed via the open lean PR or - `unusedFintypeInType` lands with a workaround -/ -- Note: we don't break on errors; we want to lint even on partial declarations for t in ← getInfoTrees do for (ctx, info) in t.getDeclBodyInfos do From ebd66fd9f925ff42214e8189cca71185a7cc25cb Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:37:11 -0500 Subject: [PATCH 066/141] chore: use linter in test file in CI --- MathlibTest/OverlappingInstances.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 6b2c43e8c309d5..0d02c93649fd33 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -4,6 +4,7 @@ import Mathlib.Tactic.Linter.OverlappingInstances import Mathlib.Tactic.TypeStar set_option linter.overlappingInstances true +set_option linter.overlappingInstances.onlyInServer false namespace Lean From be119987ab43f442a5c0cf3aba7183008b036ea2 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 11:37:36 -0500 Subject: [PATCH 067/141] chore: test `set_option ... in` --- MathlibTest/OverlappingInstances.lean | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 0d02c93649fd33..3aec1cdbbc9c5d 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -159,3 +159,23 @@ There should only be a single instance of these data-carrying typeclasses in the def needsInstantiateMVars [Repr α] : Bool := true end instantiateMVars + +section setOptionIn + +set_option linter.overlappingInstances false in +def fooNothing [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := true + +set_option linter.overlappingInstances false + +/-- +warning: The declaration `fooSomething` has instance hypotheses which provide conflicting versions of the same data. Specifically: + +There are 4 instances of `[Add Nat]`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `fooSomething`. +-/ +#guard_msgs in +set_option linter.overlappingInstances true in +def fooSomething [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := true + +end setOptionIn From 2562644c0d097a82c264d643d5d5eb34df3840f0 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 12:39:25 -0500 Subject: [PATCH 068/141] chore: note about running interactively --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 0528450c37fdfb..2b4d94198699e3 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -306,7 +306,12 @@ def overlappingInstances : Linter where let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant - logWarning <|← overlaps.toMsg <|← ctx.toDeclDescr + let mut msg ← overlaps.toMsg <|← ctx.toDeclDescr + if ← linter.overlappingInstances.onlyInServer.getM then + msg := msg ++ .note "This linter is only run interactively by default for \ + performance reasons. To run it noninteractively, use \ + `set_option linter.overlappingInstances.onlyInServer false`." + logWarning msg initialize addLinter overlappingInstances From 3ea1ef1469cc76451b4572dc26a10a939c36299e Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 12:49:23 -0500 Subject: [PATCH 069/141] chore: remove stale set_option comment --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 -- 1 file changed, 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 2b4d94198699e3..a9a169e80a835b 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -280,8 +280,6 @@ where open Linter in /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. - -Note: currently does not respect `set_option`. -/ def overlappingInstances : Linter where run := withSetBoolOptionIn fun cmd => do From 5583e063a7da516776b04cdb81d97a96be0fd8c6 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 13:42:19 -0500 Subject: [PATCH 070/141] feat: env linter for hybrid strategy --- .../Tactic/Linter/OverlappingInstances.lean | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a9a169e80a835b..b9543ae8fd2e00 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -313,4 +313,36 @@ def overlappingInstances : Linter where initialize addLinter overlappingInstances +open Batteries Tactic Lint + +namespace Environment + +/-- +Lints against data-carrying overlaps between instances in the local contexts of declarations. +Only considers declarations which originate in modules with the given prefix. +-/ +def overlappingInstancesInModsWithPrefix (modPrefix : Name) : Batteries.Tactic.Lint.Linter where + noErrorsFound := + m!"No declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" + errorsFound := + m!"Some declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" + test declName := do + if ← isAutoDecl declName then return none + let some s ← findModuleOf? declName | throwError "Could not find module for {declName}" + unless modPrefix.isPrefixOf s do return none + MetaM.run' do + forallTelescope (← getConstInfo declName).type fun _ _ => do + let overlaps ← findOverlappingDataInstances + if overlaps.isEmpty then return none else + some <$> overlaps.toMsg m!"declaration {.ofConstName declName}" + +/-- +Lints against data-carrying overlaps between instances in the local contexts of declarations. +Only considers declarations which originate in Mathlib. +-/ +@[env_linter] +def overlappingInstancesInMathlib := overlappingInstancesInModsWithPrefix `Mathlib + +end Environment + end Mathlib.Linter.OverlappingInstances From b18eef2e5225b9efb67db39f38d9bd86f4fb8b43 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 17:37:37 -0500 Subject: [PATCH 071/141] chore: fix `#lint` test --- MathlibTest/HashLint.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MathlibTest/HashLint.lean b/MathlibTest/HashLint.lean index 2e6100eee0d12b..c42c35e1841fd8 100644 --- a/MathlibTest/HashLint.lean +++ b/MathlibTest/HashLint.lean @@ -6,7 +6,7 @@ import Mathlib.Init /-! Checks that a basic `#lint` works. -/ /-- -info: -- Found 0 errors in 0 declarations (plus 0 automatically generated ones) in the current file with 16 linters +info: -- Found 0 errors in 0 declarations (plus 0 automatically generated ones) in the current file with 17 linters -- All linting checks passed! From c2c351023bf2c0258cc9ba8c34131073885cc01b Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:05:05 -0500 Subject: [PATCH 072/141] chore: fix or exempt declarations (two) --- Mathlib/Algebra/Module/Lattice.lean | 2 +- Mathlib/Algebra/Order/Antidiag/Prod.lean | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Mathlib/Algebra/Module/Lattice.lean b/Mathlib/Algebra/Module/Lattice.lean index abd87b0c5e2a33..c932b303326319 100644 --- a/Mathlib/Algebra/Module/Lattice.lean +++ b/Mathlib/Algebra/Module/Lattice.lean @@ -66,7 +66,7 @@ Note 2: In the case `R = ℤ` and `A = K` a field, there is also `IsZLattice` wh generated condition is replaced by having the discrete topology. -/ class IsLattice (A : outParam Type*) [CommRing A] [Algebra R A] {V : Type*} [AddCommMonoid V] [Module R V] [Module A V] [IsScalarTower R A V] - [Algebra R A] [IsScalarTower R A V] (M : Submodule R V) : Prop where + [IsScalarTower R A V] (M : Submodule R V) : Prop where fg : M.FG span_eq_top : Submodule.span A (M : Set V) = ⊤ diff --git a/Mathlib/Algebra/Order/Antidiag/Prod.lean b/Mathlib/Algebra/Order/Antidiag/Prod.lean index f333dc4ac64ea3..514a8b6c6f4f95 100644 --- a/Mathlib/Algebra/Order/Antidiag/Prod.lean +++ b/Mathlib/Algebra/Order/Antidiag/Prod.lean @@ -77,6 +77,7 @@ instance [AddMonoid A] : Subsingleton (HasAntidiagonal A) where -- The goal of this lemma is to allow to rewrite antidiagonal -- when the decidability instances obsucate Lean +@[nolint overlappingInstancesInMathlib] lemma hasAntidiagonal_congr (A : Type*) [AddMonoid A] [H1 : HasAntidiagonal A] [H2 : HasAntidiagonal A] : H1.antidiagonal = H2.antidiagonal := by congr!; subsingleton From c28f197c581d3a65033887afa58ba1d660ddef61 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:52:29 -0500 Subject: [PATCH 073/141] chore: todo --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index b9543ae8fd2e00..06d031baafcd7f 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -36,6 +36,7 @@ but they are still duplicated in the local context while editing the body. ## TODO - Improve performance. Currently running this linter in CI is prohibitively expensive. +- Expand to declarations without bodies (`structure`s/`class`es/`inductive`s etc.) - The logging location for this linter could be improved. - Currently it is possible to obtain a message which includes something of the following form: ``` From e41f371699ce85acaeeebff4ac2c3f7e8227cd60 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 10 Feb 2026 18:57:20 -0500 Subject: [PATCH 074/141] temp: bench old version --- .../Tactic/Linter/OverlappingInstances.lean | 101 ++++++++++++++---- 1 file changed, 78 insertions(+), 23 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 06d031baafcd7f..627455013e4a62 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -318,31 +318,86 @@ open Batteries Tactic Lint namespace Environment -/-- -Lints against data-carrying overlaps between instances in the local contexts of declarations. -Only considers declarations which originate in modules with the given prefix. --/ -def overlappingInstancesInModsWithPrefix (modPrefix : Name) : Batteries.Tactic.Lint.Linter where - noErrorsFound := - m!"No declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" - errorsFound := - m!"Some declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" - test declName := do +section bench + +/-- Given an instance `e`, conpute return all data carrying classes that are +the type of `e` itself, or a child class. -/ +private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := #[]) : + StateRefT NameSet MetaM (Array Expr) := do + let eType ← whnf (← inferType e) + if ← isProp eType then return acc + let .const structName us := eType.getForallBody.getAppFn + | throwError "{e} is not an instance of a structure" + if (← get).contains structName then return acc + modify (·.insert structName) + let some info := getStructureInfo? (← getEnv) structName | return acc + info.parentInfo.foldlM (init := acc.push eType) fun acc info ↦ do + if ← isInstance info.projFn then + let proj := Expr.app (mkAppN (.const info.projFn us) eType.getAppArgs) e + getStructureDataProjections proj acc + else + return acc + +/-- Run the overlapping instances linter on `declName`. -/ +def findDuplicateDataInstances (declName : Name) : CoreM (Option MessageData) := MetaM.run' do + Core.checkSystem "overlappingInstances" + forallTelescope (← getConstInfo declName).type fun _ _ ↦ do + let mut result := none + let mut insts : Std.HashMap Expr Expr := {} + for { fvar, .. } in ← getLocalInstances do + unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue + let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do + (← getStructureDataProjections (mkAppN fvar xs) |>.run' {}).mapM (mkForallFVars xs) + + for cls in projClasses do + if let some fvar' := insts[cls]? then + let type ← inferType fvar; let type' ← inferType fvar' + if type == type' then + result := m!"{result.getD m!""}There are multiple different instances of `{type}`\n\n" + break + else + result := m!"{result.getD m!""}`{type}` and `{type'}` both imply `{cls}`\n\n" + else + insts := insts.insert cls fvar + result.mapM addMessageContextFull + +/-- A linter for declarations which have instance hypotheses that have overlapping data. -/ +@[env_linter] def overlappingInstancesInMathlib : Lint.Linter where + noErrorsFound := "No declarations have overlapping instance arguments" + errorsFound := "Some declarations have overlapping instance arguments" + test := fun declName => do if ← isAutoDecl declName then return none let some s ← findModuleOf? declName | throwError "Could not find module for {declName}" - unless modPrefix.isPrefixOf s do return none - MetaM.run' do - forallTelescope (← getConstInfo declName).type fun _ _ => do - let overlaps ← findOverlappingDataInstances - if overlaps.isEmpty then return none else - some <$> overlaps.toMsg m!"declaration {.ofConstName declName}" - -/-- -Lints against data-carrying overlaps between instances in the local contexts of declarations. -Only considers declarations which originate in Mathlib. --/ -@[env_linter] -def overlappingInstancesInMathlib := overlappingInstancesInModsWithPrefix `Mathlib + unless (`Mathlib).isPrefixOf s do return none + findDuplicateDataInstances declName + +end bench + +-- /-- +-- Lints against data-carrying overlaps between instances in the local contexts of declarations. +-- Only considers declarations which originate in modules with the given prefix. +-- -/ +-- def overlappingInstancesInModsWithPrefix (modPrefix : Name) : Batteries.Tactic.Lint.Linter where +-- noErrorsFound := +-- m!"No declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" +-- errorsFound := +-- m!"Some declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" +-- test declName := do +-- if ← isAutoDecl declName then return none +-- let some s ← findModuleOf? declName | throwError "Could not find module for {declName}" +-- unless modPrefix.isPrefixOf s do return none +-- MetaM.run' do +-- forallTelescope (← getConstInfo declName).type fun _ _ => do +-- let overlaps ← findOverlappingDataInstances +-- if overlaps.isEmpty then return none else +-- some <$> overlaps.toMsg m!"declaration {.ofConstName declName}" + +-- /-- +-- Lints against data-carrying overlaps between instances in the local contexts of declarations. +-- Only considers declarations which originate in Mathlib. +-- -/ +-- @[env_linter] +-- def overlappingInstancesInMathlib := overlappingInstancesInModsWithPrefix `Mathlib end Environment From 8f60681ef39cbd250121acc6301b34dbf25a503c Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Mon, 16 Feb 2026 00:42:30 -0500 Subject: [PATCH 075/141] Revert "temp: bench old version" This reverts commit e41f371699ce85acaeeebff4ac2c3f7e8227cd60. --- .../Tactic/Linter/OverlappingInstances.lean | 101 ++++-------------- 1 file changed, 23 insertions(+), 78 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 627455013e4a62..06d031baafcd7f 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -318,86 +318,31 @@ open Batteries Tactic Lint namespace Environment -section bench - -/-- Given an instance `e`, conpute return all data carrying classes that are -the type of `e` itself, or a child class. -/ -private partial def getStructureDataProjections (e : Expr) (acc : Array Expr := #[]) : - StateRefT NameSet MetaM (Array Expr) := do - let eType ← whnf (← inferType e) - if ← isProp eType then return acc - let .const structName us := eType.getForallBody.getAppFn - | throwError "{e} is not an instance of a structure" - if (← get).contains structName then return acc - modify (·.insert structName) - let some info := getStructureInfo? (← getEnv) structName | return acc - info.parentInfo.foldlM (init := acc.push eType) fun acc info ↦ do - if ← isInstance info.projFn then - let proj := Expr.app (mkAppN (.const info.projFn us) eType.getAppArgs) e - getStructureDataProjections proj acc - else - return acc - -/-- Run the overlapping instances linter on `declName`. -/ -def findDuplicateDataInstances (declName : Name) : CoreM (Option MessageData) := MetaM.run' do - Core.checkSystem "overlappingInstances" - forallTelescope (← getConstInfo declName).type fun _ _ ↦ do - let mut result := none - let mut insts : Std.HashMap Expr Expr := {} - for { fvar, .. } in ← getLocalInstances do - unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue - let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do - (← getStructureDataProjections (mkAppN fvar xs) |>.run' {}).mapM (mkForallFVars xs) - - for cls in projClasses do - if let some fvar' := insts[cls]? then - let type ← inferType fvar; let type' ← inferType fvar' - if type == type' then - result := m!"{result.getD m!""}There are multiple different instances of `{type}`\n\n" - break - else - result := m!"{result.getD m!""}`{type}` and `{type'}` both imply `{cls}`\n\n" - else - insts := insts.insert cls fvar - result.mapM addMessageContextFull - -/-- A linter for declarations which have instance hypotheses that have overlapping data. -/ -@[env_linter] def overlappingInstancesInMathlib : Lint.Linter where - noErrorsFound := "No declarations have overlapping instance arguments" - errorsFound := "Some declarations have overlapping instance arguments" - test := fun declName => do +/-- +Lints against data-carrying overlaps between instances in the local contexts of declarations. +Only considers declarations which originate in modules with the given prefix. +-/ +def overlappingInstancesInModsWithPrefix (modPrefix : Name) : Batteries.Tactic.Lint.Linter where + noErrorsFound := + m!"No declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" + errorsFound := + m!"Some declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" + test declName := do if ← isAutoDecl declName then return none let some s ← findModuleOf? declName | throwError "Could not find module for {declName}" - unless (`Mathlib).isPrefixOf s do return none - findDuplicateDataInstances declName - -end bench - --- /-- --- Lints against data-carrying overlaps between instances in the local contexts of declarations. --- Only considers declarations which originate in modules with the given prefix. --- -/ --- def overlappingInstancesInModsWithPrefix (modPrefix : Name) : Batteries.Tactic.Lint.Linter where --- noErrorsFound := --- m!"No declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" --- errorsFound := --- m!"Some declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" --- test declName := do --- if ← isAutoDecl declName then return none --- let some s ← findModuleOf? declName | throwError "Could not find module for {declName}" --- unless modPrefix.isPrefixOf s do return none --- MetaM.run' do --- forallTelescope (← getConstInfo declName).type fun _ _ => do --- let overlaps ← findOverlappingDataInstances --- if overlaps.isEmpty then return none else --- some <$> overlaps.toMsg m!"declaration {.ofConstName declName}" - --- /-- --- Lints against data-carrying overlaps between instances in the local contexts of declarations. --- Only considers declarations which originate in Mathlib. --- -/ --- @[env_linter] --- def overlappingInstancesInMathlib := overlappingInstancesInModsWithPrefix `Mathlib + unless modPrefix.isPrefixOf s do return none + MetaM.run' do + forallTelescope (← getConstInfo declName).type fun _ _ => do + let overlaps ← findOverlappingDataInstances + if overlaps.isEmpty then return none else + some <$> overlaps.toMsg m!"declaration {.ofConstName declName}" + +/-- +Lints against data-carrying overlaps between instances in the local contexts of declarations. +Only considers declarations which originate in Mathlib. +-/ +@[env_linter] +def overlappingInstancesInMathlib := overlappingInstancesInModsWithPrefix `Mathlib end Environment From 344c1fdf134810ec61fee2a1a95c69315f426c8e Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Mon, 16 Feb 2026 11:47:43 -0500 Subject: [PATCH 076/141] chore: don't manually limit to prefix --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 06d031baafcd7f..41485422115582 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -320,30 +320,21 @@ namespace Environment /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -Only considers declarations which originate in modules with the given prefix. -/ -def overlappingInstancesInModsWithPrefix (modPrefix : Name) : Batteries.Tactic.Lint.Linter where +@[env_linter] +def overlappingInstances : Batteries.Tactic.Lint.Linter where noErrorsFound := - m!"No declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" + m!"No declarations have overlapping instance arguments" errorsFound := - m!"Some declarations in modules with prefix `{modPrefix}` have overlapping instance arguments" + m!"Some declarations have overlapping instance arguments" test declName := do if ← isAutoDecl declName then return none - let some s ← findModuleOf? declName | throwError "Could not find module for {declName}" - unless modPrefix.isPrefixOf s do return none MetaM.run' do forallTelescope (← getConstInfo declName).type fun _ _ => do let overlaps ← findOverlappingDataInstances if overlaps.isEmpty then return none else some <$> overlaps.toMsg m!"declaration {.ofConstName declName}" -/-- -Lints against data-carrying overlaps between instances in the local contexts of declarations. -Only considers declarations which originate in Mathlib. --/ -@[env_linter] -def overlappingInstancesInMathlib := overlappingInstancesInModsWithPrefix `Mathlib - end Environment end Mathlib.Linter.OverlappingInstances From 30ebadc6b06825783b89fe4423cd3d4b66cc38aa Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Mon, 16 Feb 2026 12:12:03 -0500 Subject: [PATCH 077/141] chore: fix nolint --- Mathlib/Algebra/Order/Antidiag/Prod.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Algebra/Order/Antidiag/Prod.lean b/Mathlib/Algebra/Order/Antidiag/Prod.lean index 514a8b6c6f4f95..02b9f4523616cc 100644 --- a/Mathlib/Algebra/Order/Antidiag/Prod.lean +++ b/Mathlib/Algebra/Order/Antidiag/Prod.lean @@ -77,7 +77,7 @@ instance [AddMonoid A] : Subsingleton (HasAntidiagonal A) where -- The goal of this lemma is to allow to rewrite antidiagonal -- when the decidability instances obsucate Lean -@[nolint overlappingInstancesInMathlib] +@[nolint overlappingInstances] lemma hasAntidiagonal_congr (A : Type*) [AddMonoid A] [H1 : HasAntidiagonal A] [H2 : HasAntidiagonal A] : H1.antidiagonal = H2.antidiagonal := by congr!; subsingleton From 0b8aaa37de4caf39a84e87ad3c131e73aea2d816 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Mon, 16 Feb 2026 12:12:35 -0500 Subject: [PATCH 078/141] chore: also set_option --- Mathlib/Algebra/Order/Antidiag/Prod.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/Algebra/Order/Antidiag/Prod.lean b/Mathlib/Algebra/Order/Antidiag/Prod.lean index 02b9f4523616cc..7c93c36f330b38 100644 --- a/Mathlib/Algebra/Order/Antidiag/Prod.lean +++ b/Mathlib/Algebra/Order/Antidiag/Prod.lean @@ -77,6 +77,7 @@ instance [AddMonoid A] : Subsingleton (HasAntidiagonal A) where -- The goal of this lemma is to allow to rewrite antidiagonal -- when the decidability instances obsucate Lean +set_option linter.overlappingInstances false in @[nolint overlappingInstances] lemma hasAntidiagonal_congr (A : Type*) [AddMonoid A] [H1 : HasAntidiagonal A] [H2 : HasAntidiagonal A] : From ea7abe060c3ffa3f85bb6010de2fd992374a3050 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 18 Feb 2026 12:55:25 -0500 Subject: [PATCH 079/141] chore: docs --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 41485422115582..b25bba1455f086 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -78,7 +78,7 @@ private partial def getClassDataProjections (e : Expr) (acc : Array (List Nat × -- `e` has already been recorded, but is being encountered as the child of a new parent at -- `parentIdx`. Add this parent index to `e`'s original parent indices. return acc.modify structIdx fun (parents, struct) => (parentIdx :: parents, struct) - -- In ordinary usage, where only the first invocation of `getStructureDataProjections` has no + -- In ordinary usage, where only the first invocation of `getClassDataProjections` has no -- parent, this case cannot be reached. return acc -- Record current class and recurse @@ -197,7 +197,8 @@ register_option linter.overlappingInstances.onlyInServer : Bool := { } /-- -Creates a description of the current declaration in messages: "declaration ``" if the `parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". +Creates a description of the current declaration in messages: "declaration ``" if the +`parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". TODO: For now, this does not produce hovers on ``, since the name may clash with the aux decl of the same name in the local context. In the future, we should account for this, and render From 2721676730d578cdb08c83a46f6c10f6348344e9 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 18 Feb 2026 12:58:15 -0500 Subject: [PATCH 080/141] chore docs --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index b25bba1455f086..ba2f7df4b18223 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -18,11 +18,10 @@ non-defeq versions of that data. This situation, both for declarations and more as an "instance diamond". This linter warns against declarations whose local contexts include multiple versions of the same data. -This is a syntax linter intended for linting declaration bodies that appear in source. It does not -lint autogenerated declarations or declarations from imported modules. - -For performance reasons, this linter **only** runs interactively in the language server. It will -not run on the command line during a typical `lake build`. +This is a hybrid syntax and environment linter. The syntax linter only lints the bodies of +declarations that appear in source. For performance reasons, the syntax linter **only** runs +interactively in the language server. It will not run on the command line during a typical +`lake build`. Note that since all proofs of a given proposition are definitionally equal, multiple different ways of obtaining instances of `Prop` classes pose no issue. Hence, this linter only warns against @@ -310,7 +309,8 @@ def overlappingInstances : Linter where if ← linter.overlappingInstances.onlyInServer.getM then msg := msg ++ .note "This linter is only run interactively by default for \ performance reasons. To run it noninteractively, use \ - `set_option linter.overlappingInstances.onlyInServer false`." + `set_option linter.overlappingInstances.onlyInServer false`, or use the + corresponding `overlappingInstances` environment linter through `#lint`." logWarning msg initialize addLinter overlappingInstances From 967f800e1deb7d192d23fca39ab8ce9f25c9fad2 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 18 Feb 2026 13:00:10 -0500 Subject: [PATCH 081/141] chore: tweak docs --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index ba2f7df4b18223..73b857f8c2d29f 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -18,10 +18,11 @@ non-defeq versions of that data. This situation, both for declarations and more as an "instance diamond". This linter warns against declarations whose local contexts include multiple versions of the same data. -This is a hybrid syntax and environment linter. The syntax linter only lints the bodies of -declarations that appear in source. For performance reasons, the syntax linter **only** runs -interactively in the language server. It will not run on the command line during a typical -`lake build`. +This is a hybrid syntax and environment linter.For performance reasons, the syntax linter **only** +runs interactively in the language server. It will not run on the command line during a typical +`lake build`. The syntax linter also only lints the bodies of declarations that appear in source, +and does not currently handle declarations that do not have a "body" such as `structure`s. (The +environment linter does handle these cases.) Note that since all proofs of a given proposition are definitionally equal, multiple different ways of obtaining instances of `Prop` classes pose no issue. Hence, this linter only warns against From af29528fce6824d5c6b975b8a20b23c6f7172f9b Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Tue, 24 Feb 2026 14:40:57 -0500 Subject: [PATCH 082/141] chore: update copyright since file is being re-added --- Mathlib/Lean/Message.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Lean/Message.lean b/Mathlib/Lean/Message.lean index 2622e92a0c4c41..76de10d483aa24 100644 --- a/Mathlib/Lean/Message.lean +++ b/Mathlib/Lean/Message.lean @@ -1,5 +1,5 @@ /- -Copyright (c) 2022 Floris van Doorn. All rights reserved. +Copyright (c) 2026 Thomas R Murrills. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Thomas R. Murrills -/ From b2aff5a76b44b9dad614c85d4ae7d69dd1800fbf Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 16 Apr 2026 17:46:31 +0200 Subject: [PATCH 083/141] new version --- Mathlib/Lean/ContextInfo.lean | 22 +- Mathlib/Lean/Elab/InfoTree.lean | 14 +- Mathlib/Lean/Message.lean | 25 -- .../Tactic/Linter/OverlappingInstances.lean | 331 +++++------------- .../Tactic/Linter/UnusedInstancesInType.lean | 2 +- MathlibTest/OverlappingInstances.lean | 42 ++- 6 files changed, 128 insertions(+), 308 deletions(-) delete mode 100644 Mathlib/Lean/Message.lean diff --git a/Mathlib/Lean/ContextInfo.lean b/Mathlib/Lean/ContextInfo.lean index a722747d9510f6..417a65ba56dfda 100644 --- a/Mathlib/Lean/ContextInfo.lean +++ b/Mathlib/Lean/ContextInfo.lean @@ -58,23 +58,15 @@ def runCoreMWithMessages (info : ContextInfo) (x : CoreM α) : CommandElabM α : Copy of `ContextInfo.runMetaM` that makes use of the `CommandElabM` context for: * message logging (messages produced by the `CoreM` action are migrated back), * metavariable generation, -* auxiliary declaration generation - -If `localInstances := none` (the default), `runMetaMWithMessages` will refresh the local instances -in the context. +* auxiliary declaration generation, +* local instances. -/ -def runMetaMWithMessages (info : ContextInfo) (lctx : LocalContext) (x : MetaM α) - (localInstances : Option LocalInstances := none) : CommandElabM α := do +def runMetaMWithMessages (info : ContextInfo) (lctx : LocalContext) (x : MetaM α) : CommandElabM α := do (·.1) <$> info.runCoreMWithMessages (Lean.Meta.MetaM.run - -- Use provided local instances here to avoid a closure. - (ctx := { lctx := lctx, localInstances := localInstances.getD #[] }) - (s := { mctx := info.mctx }) <| - if localInstances.isSome then - x - else - -- Update the local instances, otherwise typeclass search would fail to see anything in the - -- local context. - Meta.withLocalInstances (lctx.decls.toList.filterMap id) <| x) + (ctx := { lctx := lctx }) (s := { mctx := info.mctx }) <| + -- Update the local instances, otherwise typeclass search would fail to see anything in the + -- local context. + Meta.withLocalInstances (lctx.decls.toList.filterMap id) <| x) /-- Run a tactic computation in the context of an infotree node. -/ def runTactic (ctx : ContextInfo) (i : TacticInfo) (goal : MVarId) (x : MVarId → MetaM α) : diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index 5036c91a4be3dd..9755b678eea600 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -133,7 +133,7 @@ def getDeclsByBody (t : InfoTree) : List Name := /-- Gets the first child info of each `Lean.Elab.BodyInfo`, which should be the only child, and should be a `TermInfo`, `PartialTermInfo`, or `TacticInfo`. `getDeclBodyInfos` does not validate either of these conditions. -/ -def getDeclBodyInfos (t : InfoTree) : List (ContextInfo × Info) := +def getDeclBodyInfos (t : InfoTree) : List (Syntax × ContextInfo × Info) := t.foldInfoTree (init := []) fun ctx t acc => match t with | .node (.ofCustomInfo i) body => Id.run do @@ -142,7 +142,7 @@ def getDeclBodyInfos (t : InfoTree) : List (ContextInfo × Info) := -- See through `.context`s instead of just matching on `.node`: let result? := body[0].getHighestInfo? ctx if let some result := result? then - return result :: acc + return (i.stx, result) :: acc return acc | _ => acc @@ -158,17 +158,17 @@ end InfoTree namespace Info -/-- Gets the local context, the local instances if available, and the expected type just before -`Expr`. Handles `TacticInfo`s (looking at the first goal), `TermInfo`s, and `PartialTermInfo`s. +/-- Gets the local context, and the expected type of the `Info`. +Handles `TacticInfo`s (looking at the first goal), `TermInfo`s, and `PartialTermInfo`s. Does not get the metavariable context; assumes that the caller has accumulated an ambient `ContextInfo` at this point which is sufficient. -/ -def getLCtxBefore? : Info → Option (LocalContext × Option LocalInstances × Option Expr) +def getLCtx? : Info → Option (LocalContext × Option Expr) | .ofTacticInfo i => do let g ← i.goalsBefore.head? let decl ← i.mctxBefore.findDecl? g - some (decl.lctx, decl.localInstances, decl.type) + some (decl.lctx, decl.type) | .ofTermInfo i - | .ofPartialTermInfo i => some (i.lctx, none, i.expectedType?) + | .ofPartialTermInfo i => some (i.lctx, i.expectedType?) | _ => none end Lean.Elab.Info diff --git a/Mathlib/Lean/Message.lean b/Mathlib/Lean/Message.lean deleted file mode 100644 index 76de10d483aa24..00000000000000 --- a/Mathlib/Lean/Message.lean +++ /dev/null @@ -1,25 +0,0 @@ -/- -Copyright (c) 2026 Thomas R Murrills. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Thomas R. Murrills --/ -module -- shake: keep-all - -public import Lean.Message --- Import this linter explicitly to ensure that --- this file has a valid copyright header and module docstring. -public import Mathlib.Tactic.Linter.Header - -/-! -# Additional utilities for `MessageData` --/ - -public section - -namespace Lean.MessageData - -/-- Renders an expression `Foo` as an instance binder `[]`, taking care to group and nest -appropriately. This is mostly a synonym for `MessageData.sbracket` for discoverability. -/ -def ofInstanceBinderType (e : Expr) : MessageData := m!"{e}".sbracket - -end Lean.MessageData diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 73b857f8c2d29f..60cc203eba7a75 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -8,7 +8,8 @@ module public meta import Lean.Elab.Command public meta import Mathlib.Lean.ContextInfo public meta import Mathlib.Lean.Elab.InfoTree -public meta import Mathlib.Lean.Message + +public meta import Mathlib.Tactic.Linter.UnusedInstancesInType /-! # A linter for declarations with local instances that have overlapping data @@ -54,131 +55,81 @@ but they are still duplicated in the local context while editing the body. open Lean Meta Elab Command -public meta section +meta section namespace Mathlib.Linter.OverlappingInstances -/-- -Given an instance `e`, compute all data carrying classes that are -the type of `e` itself, or a child class, together with the indices of the parents of each -projection as they appear in this array. - -This also records data-carrying non-structure inductive classes in a one-element array. --/ -private partial def getClassDataProjections (e : Expr) (acc : Array (List Nat × Expr) := #[]) - (parentIdx? : Option Nat := none) : - StateRefT (NameMap Nat) MetaM (Array (List Nat × Expr)) := do - let eType ← whnf <|← instantiateMVars <|← inferType e - if ← isProp eType then return acc - let .const structName us := eType.getForallBody.getAppFn - | throwError "`{e}` is not an instance of a structure" - -- Check if already recorded - if let some structIdx := (← get).get? structName then - if let some parentIdx := parentIdx? then - -- `e` has already been recorded, but is being encountered as the child of a new parent at - -- `parentIdx`. Add this parent index to `e`'s original parent indices. - return acc.modify structIdx fun (parents, struct) => (parentIdx :: parents, struct) - -- In ordinary usage, where only the first invocation of `getClassDataProjections` has no - -- parent, this case cannot be reached. - return acc - -- Record current class and recurse - let currentIdx := acc.size - let acc := acc.push (parentIdx?.toList, eType) - if let some info := getStructureInfo? (← getEnv) structName then - -- Record the index at which this structure occurs in `acc`, so we can add to its parents later - -- if it is encountered as a projection of something else. - modify (·.insert structName currentIdx) - info.parentInfo.foldlM (init := acc) fun acc info ↦ do - if ← isInstance info.projFn then - let proj := Expr.app (mkAppN (.const info.projFn us) eType.getAppArgs) e - getClassDataProjections proj acc currentIdx - else - return acc - else - -- This case should only be reached by non-structure inductive classes, and therefore only - -- occur at the root. We still want to record these to warn on duplicate inductive classes. +/-- Clear the instances from the given application. -/ +def eraseInstances (e : Expr) : MetaM Expr := do + e.withApp fun f args ↦ do + let finfo ← getFunInfo f + let mut args := args + for param in finfo.paramInfo, i in *...args.size do + if param.binderInfo.isInstImplicit then + args := args.set! i default + return mkAppN f args + +/-- Compute the data projections of the class `cls`. +If `cls` is a `Prop` or a non-structure class, then return singleton array with just `cls`. +The results contain bound variables corresponding to the parameters of `cls`. -/ +partial def abstractDataProjections (cls : Name) : CoreM (Array Expr) := do + let cinfo ← getConstInfo cls + MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do + withLocalDeclD `self (mkAppN (.const cls (cinfo.levelParams.map .param)) xs) fun inst ↦ do + go cls inst #[] xs |>.run' {} +where + go (cls : Name) (inst : Expr) (acc : Array Expr) (xs : Array Expr) : + StateRefT NameSet MetaM (Array Expr) := do + let type ← whnf (← inferType inst) + let mut acc := acc + let mut anyParent := false + if let some info := getStructureInfo? (← getEnv) cls then + let .const _ us := type.getAppFn | panic! s!"`{inst} is not an instance" + for info in info.parentInfo do + let parent := info.structName + if (← get).contains parent then continue + modify (·.insert parent) + if (← getConstInfo parent).type.getForallBody.isProp then continue + let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst + acc ← go parent proj acc xs + anyParent := true + if !anyParent then + acc := acc.push (← eraseInstances (type.abstract xs)) return acc -/-- Given an array of projection types paired with the locations of their parents, returns `true` -if `p` is true for the types at any of the starting indices or their transitive parents. -/ -private partial def hasAnyParentWhich (p : Expr → Bool) - (projections : Array (List Nat × Expr)) (startingIdxs : List Nat) : Bool := - startingIdxs.any fun idx => - let (parentIdxs, cls) := projections[idx]! - p cls || hasAnyParentWhich p projections parentIdxs - -/-- Stores the local instance overlaps per class. The "keys" are the class, and the values are local -instances which have the class as a projection. The `Bool` in the value of each entry indicates -whether its type is exactly the key class. - -The code constructing values of this class is responsible for ensuring that (1) every `Array` value -contains at least two elements (2) no element of the array appears twice. +initialize dataProjectionCache : EnvExtension (NameMap (Array Expr)) ← + registerEnvExtension (pure {}) -We use an `Array` instead of a hashmap in order to record the overlaps in the order the classes -appear. -/ -abbrev Overlaps := Array <| Expr × Array (Expr × Bool) - -/-- Inserts `fvar₁` into the overlap for `cls` with `fvar₀`, assuming `fvar₀` is the representative -of the class. Since this is the only way we insert fvars and the representatives do not change, we -assume the representative `fvar₀` has already been inserted if the overlap exists, and do not -re-insert it. -/ -def Overlaps.pushOverlap (fvar₀ : Expr × Bool) (cls : Expr) (fvar₁ : Expr × Bool) - (overlaps : Overlaps) : Overlaps := - match overlaps.findIdx? (·.1 == cls) with - | none => overlaps.push (cls, #[fvar₀, fvar₁]) - | some idx => overlaps.modify idx fun (cls, overlap) => (cls, overlap.push fvar₁) - -/-- Returns `true` iff `fvar` is among the overlaps recorded for `cls`. -/ -def Overlaps.containsAt (cls : Expr) (fvar : Expr) (overlaps : Overlaps) : Bool := - overlaps.any fun (cls', overlap) => cls == cls' && overlap.any (·.1 == fvar) +def abstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do + if let some result := (dataProjectionCache.getState (← getEnv)).find? cls then + return result + let result ← abstractDataProjections cls + modifyEnv (dataProjectionCache.modifyState · (·.insert cls result)) + return result /-- -Find data-carrying overlaps between the current local instances of the `MetaM` context. - -The resulting `Overlaps` can be assumed to have at least two fvars present for each recorded class. -Further, it will only record overlaps at "maximal" nodes in the projection DAG; for example, if -`fvar₁` and `fvar₂` overlap on `cls`, the resulting `Overlaps` will not redundantly record their -overlap on any projection `cls.proj`. +Find classes that for which multiple different instances can be synthesized in the local context. -/ -partial def findOverlappingDataInstances : MetaM Overlaps := do - /- Records only the overlaps that will eventually be reported. This remains empty iff no - messages should be logged. -/ - let mut overlaps : Overlaps := {} - /- Associates all (data-carrying) typeclasses encountered to the first fvar that has a projection - into the typeclass. We check every projection against this hashmap to detect if it has been seen - before, and add it to the hashmap if not. - - The keys are classes, and the values are the representative fvars. The `Bool` indicates whether - the class key is exactly the type of the associated fvar. We use this for error reporting. -/ - let mut encounteredClasses : Std.HashMap Expr (Expr × Bool) := {} - for { fvar := fvar, .. } in ← getLocalInstances do - unless (← fvar.fvarId!.getBinderInfo).isInstImplicit do continue - let projClasses ← forallTelescope (← inferType fvar) fun xs _ ↦ do - (← getClassDataProjections (mkAppN fvar xs) |>.run' {}).mapM fun (parentIdx?, expr) => - return (parentIdx?, ← mkForallFVars xs expr) - for (parentIdxs, cls) in projClasses do - if let some (fvar₀, clsIsTypeOfFVar₀) := encounteredClasses[cls]? then - -- We have encountered a projection with this type already; we should now record an overlap, - -- unless it is (or will be) redundant. - -- Note that the actions in this branch are allowed to be "slow". - - /- Whether `fvar₀` yields the presciently-named class `parentClass` as a projection. This - occurs iff either - - `fvar₀` represents `parentClass` in `encounteredClasses` - - or `fvar₀` is in an overlap on `parentClass`. -/ - let isProjectionOfFVar₀ (parentClass : Expr) := - encounteredClasses[parentClass]?.any (·.1 == fvar₀) - || overlaps.containsAt parentClass fvar₀ - - /- Tests if any (strict) parents of `cls` are yielded by `fvar₀` as a projection. If so, - then `fvar` and `fvar₀` overlap (or will overlap) on some parent of the current `cls`. - We should (only) record overlaps on the maximal parent class(es); the current overlap is - therefore redundant, and we skip it. -/ - unless hasAnyParentWhich isProjectionOfFVar₀ projClasses parentIdxs do - overlaps := overlaps.pushOverlap (fvar₀, clsIsTypeOfFVar₀) cls (fvar, parentIdxs.isEmpty) - else - -- `cls` has no representative yet, so insert the current fvar as the representative. - encounteredClasses := encounteredClasses.insert cls (fvar, parentIdxs.isEmpty) +partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId)) := do + let mut overlaps : Std.HashMap Expr (Array FVarId) := {} + let mut encountered : Std.HashMap Expr FVarId := {} + for decl in ← getLCtx do + if decl.binderInfo.isInstImplicit then + let type ← instantiateMVars decl.type + let projClasses ← forallTelescopeReducing (whnfType := true) type fun xs type ↦ do + type.withApp fun f args ↦ do + let .const cls us := f | + throwError "`{decl.toExpr}` has an instance implicit binder, but it is not an instance" + let info ← getConstInfo cls + let projs ← abstractDataProjectionsCached cls + projs.mapM fun proj ↦ + mkForallFVars xs <| + (proj.instantiateLevelParams info.levelParams us).instantiate args + for cls in projClasses do + if let some fvarId' := encountered[cls]? then + overlaps := overlaps.alter cls (·.getD #[fvarId'] |>.push decl.fvarId) + else + encountered := encountered.insert cls decl.fvarId return overlaps /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ @@ -188,155 +139,49 @@ register_option linter.overlappingInstances : Bool := { overlaps and on declaration bodies." } -/-- Whether the overlapping instances linter runs only in the language server (the default). -/ -register_option linter.overlappingInstances.onlyInServer : Bool := { - defValue := true - descr := "runs the overlapping instance linter only when `Elab.inServer` - is `true`. Setting this to `false` will also run the linter during `lake build`; this is - currently discouraged for performance reasons." -} - -/-- -Creates a description of the current declaration in messages: "declaration ``" if the -`parentDecl?` is known, and "current declaration" otherwise. May be preceded by "the". - -TODO: For now, this does not produce hovers on ``, since the name may clash with the aux -decl of the same name in the local context. In the future, we should account for this, and render -the name within a more appropriate message context. The type of this declaration is therefore -subject to change. --/ -private def _root_.Lean.Elab.ContextInfo.toDeclDescr (ctx : ContextInfo) : MetaM MessageData := do - if let some decl := ctx.parentDecl? then - let decl ← unresolveNameGlobal decl - return m!"declaration `{decl}`" - else - return "current declaration" - /-- Creates a message describing the violations captured in `Overlaps`, assumed to be nonempty. -/ -def Overlaps.toMsg (declDescr : MessageData) (overlaps : Overlaps) : MetaM MessageData := do +def overlapsToMsg (overlaps : Std.HashMap Expr (Array FVarId)) (ctx : ContextInfo) : + MetaM MessageData := do + let declDescr ← + if let some decl := ctx.parentDecl? then + pure m!"declaration `{.ofConstName (← unresolveNameGlobal decl)}`" + else + pure "current declaration" let mut msg := m!"The {declDescr} \ has instance hypotheses which provide conflicting versions of the same data. Specifically:" let mut msgs := #[] for (overlap, fvars) in overlaps do - let (instsOfOverlap, parentsOfOverlap) := - fvars.toList.partitionMap fun (fvar, isFVarType) => - if isFVarType then .inl fvar else .inr fvar - let overlapType := m!"`{.ofInstanceBinderType overlap}`" - let parentTypesOfOverlap := MessageData.andList <|← parentsOfOverlap.mapM fun fvar => - return m!"`{.ofInstanceBinderType <|← inferType fvar}`" - let parentTypesOfOverlap := - m!"{if let [_, _] := parentsOfOverlap then m!"both " else m!""}{parentTypesOfOverlap}" - + let parents ← fvars.mapM fun fvarId ↦ return m!"`{.sbracket (← fvarId.getType)}`" + -- let overlap := MessageData.ofInstanceBinderType overlap msgs := msgs.push <| - if parentsOfOverlap.isEmpty then - -- Necessarily plural: - m!"There are {instsOfOverlap.length} instances of {overlapType}." - else - match instsOfOverlap with - | [] => m!"{overlapType} is provided by {parentTypesOfOverlap}." - | [_] => m!"There is an instance of {overlapType} in the local context, but it is \ - also provided by {parentTypesOfOverlap}." - | _ => m!"There are {instsOfOverlap.length} instances of {overlapType} in the local \ - context, and it is also provided by {parentTypesOfOverlap}." + m!"{.andList parents.toList} provide conflicting instances of `{overlap}`." -- Create a bulleted list if there are multiple messages, otherwise just a single line msg := if h : msgs.size = 1 then msg ++ "\n\n" ++ msgs[0] else - msgs.foldl (init := msg ++ "\n") fun accMsg newMsg => m!"{accMsg}\n• {newMsg}" + msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") msg := msg ++ m!"\n\n\ There should only be a single instance of these data-carrying typeclasses in the local context \ at a time. Consider choosing different instance hypotheses for the {declDescr}." addMessageContextFull msg -/-- This is a cheap way of getting a usefully better logging location: descend into `in`, and -remove decl modifiers. This is still not ideal (e.g. it does not take care of `mutual`, and extends -across the whole command), but prevents us from logging on e.g. the top of the docstring in most -cases. -/ -private partial def stripInAndModifiers (cmd : Syntax) : Syntax := - if cmd.isOfKind ``Parser.Command.in then - stripInAndModifiers cmd[2] - else if cmd[0].isOfKind ``Parser.Command.declModifiers then - cmd[1] - else - cmd - -/-- `withSetOptionIn` currently breaks infotree searches, so we simply set `Bool` options -until this is fixed in [lean4#11313](https://github.com/leanprover/lean4/pull/11313). - -This declaration will be removed in favor of `withSetOptionIn` when lean4#11313 lands. -/ -private partial def withSetBoolOptionIn (x : CommandElab) : CommandElab - | `(command| set_option $opt:ident $val in $cmd:command) => do - match val.raw with - | Syntax.atom _ "true" => - withBoolOption opt.getId true <| withSetBoolOptionIn x cmd - | Syntax.atom _ "false" => - withBoolOption opt.getId false <| withSetBoolOptionIn x cmd - | _ => withSetBoolOptionIn x cmd - | `(command| $_:command in $cmd:command) => withSetBoolOptionIn x cmd - | stx => x stx -where - /-- Set a `Bool` option in `CommandElabM`. Ideally, `CommandElabM` would have a - `MonadWithOptions` instance to this effect. -/ - withBoolOption (n : Name) (val : Bool) (k : CommandElabM Unit) : CommandElabM Unit := do - let opts := (← getOptions).setBool n val - Command.withScope (fun scope => { scope with opts }) k - open Linter in /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ def overlappingInstances : Linter where - run := withSetBoolOptionIn fun cmd => do + run := + UnusedInstancesInType.withSetBoolOptionIn fun cmd => do unless getLinterValue linter.overlappingInstances (← getLinterOptions) do return - -- Note that we do not consider this a linter option to avoid turning it on with `linter.all`. - if ← linter.overlappingInstances.onlyInServer.getM then - unless ← Elab.inServer.getM do - return -- Note: we don't break on errors; we want to lint even on partial declarations for t in ← getInfoTrees do - for (ctx, info) in t.getDeclBodyInfos do - let some (lctx, localInstances?, remainingType?) := info.getLCtxBefore? - | continue - -- TODO: better logging location - let outerRef := stripInAndModifiers cmd - ctx.runMetaMWithMessages lctx (localInstances := localInstances?) <| - withRef outerRef do - /- If there's a remaining expected type, then telescope into it in case it contains more - instance hypotheses. For now, we don't use the new fvars or return type for anything. -/ - remainingType?.elim id (forallTelescope · fun _ _ => ·) do - let overlaps ← findOverlappingDataInstances - unless overlaps.isEmpty do - -- TODO: alert user to `variable`s, possibly suggest `omit` when relevant - let mut msg ← overlaps.toMsg <|← ctx.toDeclDescr - if ← linter.overlappingInstances.onlyInServer.getM then - msg := msg ++ .note "This linter is only run interactively by default for \ - performance reasons. To run it noninteractively, use \ - `set_option linter.overlappingInstances.onlyInServer false`, or use the - corresponding `overlappingInstances` environment linter through `#lint`." - logWarning msg + for (ref, ctx, info) in t.getDeclBodyInfos do + let some (lctx, remainingType?) := info.getLCtx? | continue + ctx.runMetaMWithMessages lctx do withRef ref do + remainingType?.elim id (forallTelescope · fun _ _ => ·) do + let overlaps ← findOverlappingDataInstances + unless overlaps.isEmpty do + logWarning (← overlapsToMsg overlaps ctx) initialize addLinter overlappingInstances -open Batteries Tactic Lint - -namespace Environment - -/-- -Lints against data-carrying overlaps between instances in the local contexts of declarations. --/ -@[env_linter] -def overlappingInstances : Batteries.Tactic.Lint.Linter where - noErrorsFound := - m!"No declarations have overlapping instance arguments" - errorsFound := - m!"Some declarations have overlapping instance arguments" - test declName := do - if ← isAutoDecl declName then return none - MetaM.run' do - forallTelescope (← getConstInfo declName).type fun _ _ => do - let overlaps ← findOverlappingDataInstances - if overlaps.isEmpty then return none else - some <$> overlaps.toMsg m!"declaration {.ofConstName declName}" - -end Environment - end Mathlib.Linter.OverlappingInstances diff --git a/Mathlib/Tactic/Linter/UnusedInstancesInType.lean b/Mathlib/Tactic/Linter/UnusedInstancesInType.lean index 2f260cdd2459de..72e03512cdafc3 100644 --- a/Mathlib/Tactic/Linter/UnusedInstancesInType.lean +++ b/Mathlib/Tactic/Linter/UnusedInstancesInType.lean @@ -285,7 +285,7 @@ def isDecidableVariant (type : Expr) : Bool := /-- `withSetOptionIn` currently breaks infotree searches, so we simply set `Bool` options until this is fixed in [lean4#11313](https://github.com/leanprover/lean4/pull/11313). -/ -partial def withSetBoolOptionIn (x : CommandElab) : CommandElab +public partial def withSetBoolOptionIn (x : CommandElab) : CommandElab | `(command| set_option $opt:ident $val in $cmd:command) => do match val.raw with | Syntax.atom _ "true" => diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 3aec1cdbbc9c5d..3dcf2fd617aaee 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -1,10 +1,9 @@ module import Mathlib.Tactic.Linter.OverlappingInstances -import Mathlib.Tactic.TypeStar +import Mathlib.Init set_option linter.overlappingInstances true -set_option linter.overlappingInstances.onlyInServer false namespace Lean @@ -35,7 +34,7 @@ inst✝¹ inst✝ : Add Nat --- warning: The declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: -There are 4 instances of `[Add Nat]`. +`[Add Nat]`, `[Add Nat]`, `[Add Nat]`, and `[Add Nat]` provide conflicting instances of `Add Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo`. -/ @@ -45,10 +44,10 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- -@ +3:17...+4:12 +@ +3:68...+4:12 warning: The declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: -`[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaq Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` provide conflicting instances of `SubBar Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₁`. -/ @@ -61,8 +60,8 @@ set_option linter.overlappingInstances true in /-- warning: The declaration `foo₂` has instance hypotheses which provide conflicting versions of the same data. Specifically: -• There are 2 instances of `[FooBarBaz Nat]`. -• `[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaq Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` provide conflicting instances of `SubBar Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₂`. -/ @@ -72,7 +71,8 @@ def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- warning: The declaration `foo₃` has instance hypotheses which provide conflicting versions of the same data. Specifically: -There are 2 instances of `[FooBarBaz Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. +• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `SubBar Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₃`. -/ @@ -82,8 +82,8 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- warning: The declaration `foo₄` has instance hypotheses which provide conflicting versions of the same data. Specifically: -• There are 2 instances of `[FooBarBaz Nat]`. -• There is an instance of `[Bar Nat]` in the local context, but it is also provided by `[FooBarBaz Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` provide conflicting instances of `SubBar Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₄`. -/ @@ -94,8 +94,8 @@ def foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : Bool := true /-- warning: The declaration `foo₅` has instance hypotheses which provide conflicting versions of the same data. Specifically: -• `[Bar Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. -• `[Baz Nat]` is provided by both `[FooBarBaz Nat]` and `[FooBarBaz' Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` provide conflicting instances of `Baz Nat`. +• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` provide conflicting instances of `SubBar Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₅`. -/ @@ -109,7 +109,7 @@ namespace Foo /-- warning: The declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: -There are 2 instances of `[Add Nat]`. +`[Add Nat]` and `[Add Nat]` provide conflicting instances of `Add Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo`. -/ @@ -129,7 +129,7 @@ class inductive IndFoo where /-- warning: The declaration `indFoo` has instance hypotheses which provide conflicting versions of the same data. Specifically: -There are 2 instances of `[IndFoo]`. +`[IndFoo]` and `[IndFoo]` provide conflicting instances of `IndFoo`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `indFoo`. -/ @@ -139,7 +139,15 @@ def indFoo [IndFoo] [IndFoo] : Bool := true class inductive IndFooProp : Prop where | mk₁ (n : Nat) | mk₂ (b : Bool) --- Should not warn, these are props +-- We also warn when there are duplicate `Prop` clases +/-- +warning: The declaration `indFooProp` has instance hypotheses which provide conflicting versions of the same data. Specifically: + +`[IndFooProp]` and `[IndFooProp]` provide conflicting instances of `IndFooProp`. + +There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `indFooProp`. +-/ +#guard_msgs in def indFooProp [IndFooProp] [IndFooProp] : Bool := true end classInductive @@ -151,7 +159,7 @@ variable {α : Type*} [Repr α] /-- warning: The declaration `needsInstantiateMVars` has instance hypotheses which provide conflicting versions of the same data. Specifically: -There are 2 instances of `[Repr α]`. +`[Repr α]` and `[Repr α]` provide conflicting instances of `Repr α`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `needsInstantiateMVars`. -/ @@ -170,7 +178,7 @@ set_option linter.overlappingInstances false /-- warning: The declaration `fooSomething` has instance hypotheses which provide conflicting versions of the same data. Specifically: -There are 4 instances of `[Add Nat]`. +`[Add Nat]`, `[Add Nat]`, `[Add Nat]`, and `[Add Nat]` provide conflicting instances of `Add Nat`. There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `fooSomething`. -/ From 1eb3c0dc8b9f8945894d58b4399487ffe94f762a Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 16 Apr 2026 18:26:45 +0200 Subject: [PATCH 084/141] actually turn it on lol --- Mathlib/Algebra/Order/Antidiag/Prod.lean | 1 - Mathlib/Tactic/Linter/OverlappingInstances.lean | 5 ++--- MathlibTest/OverlappingInstances.lean | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Mathlib/Algebra/Order/Antidiag/Prod.lean b/Mathlib/Algebra/Order/Antidiag/Prod.lean index c8325c849af5bb..a981160070e5ec 100644 --- a/Mathlib/Algebra/Order/Antidiag/Prod.lean +++ b/Mathlib/Algebra/Order/Antidiag/Prod.lean @@ -78,7 +78,6 @@ instance [AddMonoid A] : Subsingleton (HasAntidiagonal A) where -- The goal of this lemma is to allow to rewrite antidiagonal -- when the decidability instances obfuscate Lean set_option linter.overlappingInstances false in -@[nolint overlappingInstances] lemma hasAntidiagonal_congr (A : Type*) [AddMonoid A] [H1 : HasAntidiagonal A] [H2 : HasAntidiagonal A] : H1.antidiagonal = H2.antidiagonal := by congr!; subsingleton diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 60cc203eba7a75..f1d84fd241a5a1 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -134,9 +134,8 @@ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ register_option linter.overlappingInstances : Bool := { - defValue := false - descr := "enable the overlapping instances linter. This only lints against data-carrying \ - overlaps and on declaration bodies." + defValue := true + descr := "enable the overlapping instances linter." } /-- Creates a message describing the violations captured in `Overlaps`, assumed to be nonempty. -/ diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 3dcf2fd617aaee..a7e4be99412189 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -3,8 +3,6 @@ module import Mathlib.Tactic.Linter.OverlappingInstances import Mathlib.Init -set_option linter.overlappingInstances true - namespace Lean public section From 41bd47e322a5da8a5e5ab752027567f667f5637f Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 16 Apr 2026 20:35:45 +0200 Subject: [PATCH 085/141] improvements --- .../Tactic/Linter/OverlappingInstances.lean | 57 ++++----- MathlibTest/OverlappingInstances.lean | 114 ++++++++++++++---- 2 files changed, 120 insertions(+), 51 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index f1d84fd241a5a1..cf6858079c41a5 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -59,7 +59,8 @@ meta section namespace Mathlib.Linter.OverlappingInstances -/-- Clear the instances from the given application. -/ +/-- Clear the instances from the given application. +This is used to deal with classes that have instance parameters. -/ def eraseInstances (e : Expr) : MetaM Expr := do e.withApp fun f args ↦ do let finfo ← getFunInfo f @@ -72,7 +73,7 @@ def eraseInstances (e : Expr) : MetaM Expr := do /-- Compute the data projections of the class `cls`. If `cls` is a `Prop` or a non-structure class, then return singleton array with just `cls`. The results contain bound variables corresponding to the parameters of `cls`. -/ -partial def abstractDataProjections (cls : Name) : CoreM (Array Expr) := do +partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do withLocalDeclD `self (mkAppN (.const cls (cinfo.levelParams.map .param)) xs) fun inst ↦ do @@ -97,19 +98,23 @@ where acc := acc.push (← eraseInstances (type.abstract xs)) return acc -initialize dataProjectionCache : EnvExtension (NameMap (Array Expr)) ← - registerEnvExtension (pure {}) +/-- A cache for the result of `getAbstractDataProjections`. -/ +initialize dataProjectionCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} -def abstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do - if let some result := (dataProjectionCache.getState (← getEnv)).find? cls then +/-- Return the result of `getAbstractDataProjections` while using a cache. +To ensure soundness, we only use the cache for imported classes. -/ +def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do + if (← getEnv).isImportedConst cls then + if let some result := (← dataProjectionCache.get).find? cls then + return result + else + let result ← getAbstractDataProjections cls + dataProjectionCache.modify (·.insert cls result) return result - let result ← abstractDataProjections cls - modifyEnv (dataProjectionCache.modifyState · (·.insert cls result)) - return result + else + getAbstractDataProjections cls -/-- -Find classes that for which multiple different instances can be synthesized in the local context. --/ +/-- Find classes for which multiple different instances can be synthesized in the local context. -/ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId)) := do let mut overlaps : Std.HashMap Expr (Array FVarId) := {} let mut encountered : Std.HashMap Expr FVarId := {} @@ -121,15 +126,15 @@ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId let .const cls us := f | throwError "`{decl.toExpr}` has an instance implicit binder, but it is not an instance" let info ← getConstInfo cls - let projs ← abstractDataProjectionsCached cls + let projs ← getAbstractDataProjectionsCached cls projs.mapM fun proj ↦ mkForallFVars xs <| - (proj.instantiateLevelParams info.levelParams us).instantiate args - for cls in projClasses do - if let some fvarId' := encountered[cls]? then - overlaps := overlaps.alter cls (·.getD #[fvarId'] |>.push decl.fvarId) + (proj.instantiateLevelParams info.levelParams us).instantiateRev args + for projCls in projClasses do + if let some fvarId' := encountered[projCls]? then + overlaps := overlaps.alter projCls (·.getD #[fvarId'] |>.push decl.fvarId) else - encountered := encountered.insert cls decl.fvarId + encountered := encountered.insert projCls decl.fvarId return overlaps /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ @@ -143,10 +148,10 @@ def overlapsToMsg (overlaps : Std.HashMap Expr (Array FVarId)) (ctx : ContextInf MetaM MessageData := do let declDescr ← if let some decl := ctx.parentDecl? then - pure m!"declaration `{.ofConstName (← unresolveNameGlobal decl)}`" + pure m!"Declaration `{.ofConstName (← unresolveNameGlobal decl)}`" else - pure "current declaration" - let mut msg := m!"The {declDescr} \ + pure "The current declaration" + let mut msg := m!"{declDescr} \ has instance hypotheses which provide conflicting versions of the same data. Specifically:" let mut msgs := #[] for (overlap, fvars) in overlaps do @@ -155,11 +160,9 @@ def overlapsToMsg (overlaps : Std.HashMap Expr (Array FVarId)) (ctx : ContextInf msgs := msgs.push <| m!"{.andList parents.toList} provide conflicting instances of `{overlap}`." -- Create a bulleted list if there are multiple messages, otherwise just a single line - msg := if h : msgs.size = 1 then msg ++ "\n\n" ++ msgs[0] else + msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") - msg := msg ++ m!"\n\n\ - There should only be a single instance of these data-carrying typeclasses in the local context \ - at a time. Consider choosing different instance hypotheses for the {declDescr}." + msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." addMessageContextFull msg open Linter in @@ -175,11 +178,11 @@ def overlappingInstances : Linter where for t in ← getInfoTrees do for (ref, ctx, info) in t.getDeclBodyInfos do let some (lctx, remainingType?) := info.getLCtx? | continue - ctx.runMetaMWithMessages lctx do withRef ref do + ctx.runMetaMWithMessages lctx do remainingType?.elim id (forallTelescope · fun _ _ => ·) do let overlaps ← findOverlappingDataInstances unless overlaps.isEmpty do - logWarning (← overlapsToMsg overlaps ctx) + logLint linter.overlappingInstances ref (← overlapsToMsg overlaps ctx) initialize addLinter overlappingInstances diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index a7e4be99412189..e581deb9cfd7de 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -30,11 +30,13 @@ error: unsolved goals inst✝¹ inst✝ : Add Nat ⊢ [Add Nat] → [Add Nat] → Bool --- -warning: The declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[Add Nat]`, `[Add Nat]`, `[Add Nat]`, and `[Add Nat]` provide conflicting instances of `Add Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @@ -43,11 +45,13 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- @ +3:68...+4:12 -warning: The declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[FooBarBaz Nat]` and `[FooBarBaq Nat]` provide conflicting instances of `SubBar Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₁`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs (positions := true) in set_option linter.overlappingInstances true in @@ -56,60 +60,70 @@ set_option linter.overlappingInstances true in exact true /-- -warning: The declaration `foo₂` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₂` has instance hypotheses which provide conflicting versions of the same data. Specifically: • `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. • `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` provide conflicting instances of `SubBar Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₂`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- -warning: The declaration `foo₃` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₃` has instance hypotheses which provide conflicting versions of the same data. Specifically: • `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. • `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `SubBar Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₃`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- -warning: The declaration `foo₄` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₄` has instance hypotheses which provide conflicting versions of the same data. Specifically: • `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. • `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` provide conflicting instances of `SubBar Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₄`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in -def foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : Bool := true +theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial -- Note that `[SubBar Nat]` is absent, as `[Bar Nat]` is already reported. /-- -warning: The declaration `foo₅` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₅` has instance hypotheses which provide conflicting versions of the same data. Specifically: • `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` provide conflicting instances of `Baz Nat`. • `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` provide conflicting instances of `SubBar Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo₅`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in -def foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : Bool := true +lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial namespace Foo /-! Test unresolving name (`foo`, not `Foo.foo` or `_private...foo`) -/ /-- -warning: The declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[Add Nat]` and `[Add Nat]` provide conflicting instances of `Add Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `foo`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in private def foo [Add Nat] [Add Nat] : Bool := true @@ -125,11 +139,13 @@ class inductive IndFoo where | mk₁ (n : Nat) | mk₂ (b : Bool) /-- -warning: The declaration `indFoo` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `indFoo` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[IndFoo]` and `[IndFoo]` provide conflicting instances of `IndFoo`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `indFoo`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in def indFoo [IndFoo] [IndFoo] : Bool := true @@ -139,11 +155,13 @@ class inductive IndFooProp : Prop where -- We also warn when there are duplicate `Prop` clases /-- -warning: The declaration `indFooProp` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `indFooProp` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[IndFooProp]` and `[IndFooProp]` provide conflicting instances of `IndFooProp`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `indFooProp`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in def indFooProp [IndFooProp] [IndFooProp] : Bool := true @@ -155,11 +173,13 @@ section instantiateMVars variable {α : Type*} [Repr α] /-- -warning: The declaration `needsInstantiateMVars` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `needsInstantiateMVars` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[Repr α]` and `[Repr α]` provide conflicting instances of `Repr α`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `needsInstantiateMVars`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in def needsInstantiateMVars [Repr α] : Bool := true @@ -174,14 +194,60 @@ def fooNothing [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := true set_option linter.overlappingInstances false /-- -warning: The declaration `fooSomething` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `fooSomething` has instance hypotheses which provide conflicting versions of the same data. Specifically: `[Add Nat]`, `[Add Nat]`, `[Add Nat]`, and `[Add Nat]` provide conflicting instances of `Add Nat`. -There should only be a single instance of these data-carrying typeclasses in the local context at a time. Consider choosing different instance hypotheses for the declaration `fooSomething`. +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in set_option linter.overlappingInstances true in def fooSomething [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := true end setOptionIn + +namespace universes + +class A (α : Sort u) where + +class B (α : Type u) extends A α + +/-- +warning: Declaration `_example` has instance hypotheses which provide conflicting versions of the same data. Specifically: + +`[B α]` and `[A α]` provide conflicting instances of `A α`. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example (α : Type 4) [B α] [A α] : True := trivial + +end universes + +namespace parameters + +class A (α : Type*) where +class A' (α : Type*) extends A α where + +instance {α} : A α where + +class B (α β : Type*) [A α] where +class B' (α β : Type*) [A' α] extends B α β where + +/-- +warning: Declaration `_example` has instance hypotheses which provide conflicting versions of the same data. Specifically: + +`[B α β]` and `[B' α β]` provide conflicting instances of `B α β`. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example {α β} [B α β] [A' α] [B' α β] : True := trivial + +end parameters From c4c957f71805f02b9eb9750854a88a345d6bb294 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 16 Apr 2026 22:19:06 +0200 Subject: [PATCH 086/141] some adaptations, there are so many! --- Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean | 2 +- Mathlib/Analysis/Distribution/DerivNotation.lean | 1 - Mathlib/Analysis/InnerProductSpace/GramSchmidtOrtho.lean | 4 +++- .../InnerProductSpace/Harmonic/HarmonicContOnCl.lean | 2 +- Mathlib/Analysis/Normed/Algebra/GelfandFormula.lean | 4 ++-- Mathlib/Analysis/Normed/Ring/WithAbs.lean | 3 +-- Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean | 2 +- Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean | 2 +- Mathlib/CategoryTheory/FiberedCategory/Cocartesian.lean | 3 +-- Mathlib/Combinatorics/Matroid/Rank/Cardinal.lean | 3 +-- Mathlib/Data/Finset/SMulAntidiagonal.lean | 2 +- Mathlib/FieldTheory/Finite/Extension.lean | 5 +++-- Mathlib/FieldTheory/PurelyInseparable/Exponent.lean | 2 +- Mathlib/NumberTheory/NumberField/Units/Basic.lean | 4 ++-- Mathlib/Order/CompleteLattice/PiLex.lean | 3 ++- Mathlib/Order/CompletePartialOrder.lean | 2 +- .../Process/HasIndepIncrements/IsGaussianProcess.lean | 2 +- Mathlib/RingTheory/Extension/Presentation/Submersive.lean | 2 +- Mathlib/RingTheory/Ideal/KrullsHeightTheorem.lean | 2 +- Mathlib/RingTheory/MvPowerSeries/Rename.lean | 4 ++-- Mathlib/RingTheory/SimpleModule/WedderburnArtin.lean | 2 +- Mathlib/RingTheory/Smooth/StandardSmoothCotangent.lean | 7 +++---- Mathlib/Topology/Algebra/Module/Alternating/Topology.lean | 1 - .../Algebra/Module/Spaces/PointwiseConvergenceCLM.lean | 3 +-- Mathlib/Topology/Algebra/Valued/WithVal.lean | 4 ++-- Mathlib/Topology/Convenient/GeneratedBy.lean | 2 +- Mathlib/Topology/Sion.lean | 3 +-- Mathlib/Topology/VectorBundle/Basic.lean | 2 +- 28 files changed, 37 insertions(+), 41 deletions(-) diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean index 07376bf4e749af..9ca2a4214b7965 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean @@ -655,7 +655,7 @@ variable [DecidableEq F] [DecidableEq K] [DecidableEq L] /-- The addition of two nonsingular points on a Weierstrass curve in affine coordinates. Given two nonsingular points `P` and `Q` in affine coordinates, use `P + Q` instead of `add P Q`. -/ -def add [DecidableEq F] : W.Point → W.Point → W.Point +def add : W.Point → W.Point → W.Point | 0, P => P | P, 0 => P | some x₁ y₁ h₁, some x₂ y₂ h₂ => diff --git a/Mathlib/Analysis/Distribution/DerivNotation.lean b/Mathlib/Analysis/Distribution/DerivNotation.lean index f98d4c0dfc102f..f2a875ab476c8b 100644 --- a/Mathlib/Analysis/Distribution/DerivNotation.lean +++ b/Mathlib/Analysis/Distribution/DerivNotation.lean @@ -340,7 +340,6 @@ section ContinuousLinearMap section definition variable [CommRing R] - [FiniteDimensional ℝ E] [Module R V₁] [Module R V₂] [Module R V₃] [TopologicalSpace V₁] [TopologicalSpace V₂] [TopologicalSpace V₃] [IsTopologicalAddGroup V₃] [LineDerivAdd E V₁ V₂] [LineDerivSMul R E V₁ V₂] [ContinuousLineDeriv E V₁ V₂] diff --git a/Mathlib/Analysis/InnerProductSpace/GramSchmidtOrtho.lean b/Mathlib/Analysis/InnerProductSpace/GramSchmidtOrtho.lean index dd2299104b06cd..9c7c828dbc2281 100644 --- a/Mathlib/Analysis/InnerProductSpace/GramSchmidtOrtho.lean +++ b/Mathlib/Analysis/InnerProductSpace/GramSchmidtOrtho.lean @@ -39,7 +39,7 @@ and outputs a set of orthogonal vectors which have the same span. open Finset Submodule Module variable (𝕜 : Type*) {E : Type*} [RCLike 𝕜] [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] -variable {ι : Type*} [LinearOrder ι] [LocallyFiniteOrderBot ι] [WellFoundedLT ι] +variable {ι : Type*} [LinearOrder ι] [LocallyFiniteOrderBot ι] attribute [local instance] IsWellOrder.toHasWellFounded @@ -54,6 +54,8 @@ noncomputable def gramSchmidt [WellFoundedLT ι] (f : ι → E) (n : ι) : E := termination_by n decreasing_by exact mem_Iio.1 i.2 +variable [WellFoundedLT ι] + /-- This lemma uses `∑ i in` instead of `∑ i :`. -/ theorem gramSchmidt_def (f : ι → E) (n : ι) : gramSchmidt 𝕜 f n = f n - ∑ i ∈ Iio n, (𝕜 ∙ gramSchmidt 𝕜 f i).starProjection (f n) := by diff --git a/Mathlib/Analysis/InnerProductSpace/Harmonic/HarmonicContOnCl.lean b/Mathlib/Analysis/InnerProductSpace/Harmonic/HarmonicContOnCl.lean index d885a8372fc7e1..9894ecaf6375ef 100644 --- a/Mathlib/Analysis/InnerProductSpace/Harmonic/HarmonicContOnCl.lean +++ b/Mathlib/Analysis/InnerProductSpace/Harmonic/HarmonicContOnCl.lean @@ -51,7 +51,7 @@ theorem harmonicContOnCl_const {c : F} : HarmonicContOnCl (fun _ : E ↦ c) s := namespace HarmonicContOnCl -theorem continuousOn_ball [NormedSpace ℝ E] {x : E} {r : ℝ} (h : HarmonicContOnCl f (ball x r)) : +theorem continuousOn_ball {x : E} {r : ℝ} (h : HarmonicContOnCl f (ball x r)) : ContinuousOn f (closedBall x r) := by rcases eq_or_ne r 0 with (rfl | hr) · rw [closedBall_zero] diff --git a/Mathlib/Analysis/Normed/Algebra/GelfandFormula.lean b/Mathlib/Analysis/Normed/Algebra/GelfandFormula.lean index 9869c696aa79b3..73ad28f06595e0 100644 --- a/Mathlib/Analysis/Normed/Algebra/GelfandFormula.lean +++ b/Mathlib/Analysis/Normed/Algebra/GelfandFormula.lean @@ -200,8 +200,8 @@ precisely the units. This allows for the application of this isomorphism in broa to the quotient of a complex Banach algebra by a maximal ideal. In the case when `A` is actually a `NormedDivisionRing`, one may fill in the argument `hA` with the lemma `isUnit_iff_ne_zero`. -/ @[simps] -noncomputable def _root_.NormedRing.algEquivComplexOfComplete (hA : ∀ {a : A}, IsUnit a ↔ a ≠ 0) - [CompleteSpace A] : ℂ ≃ₐ[ℂ] A := +noncomputable def _root_.NormedRing.algEquivComplexOfComplete (hA : ∀ {a : A}, IsUnit a ↔ a ≠ 0) : + ℂ ≃ₐ[ℂ] A := let nt : Nontrivial A := ⟨⟨1, 0, hA.mp ⟨⟨1, 1, mul_one _, mul_one _⟩, rfl⟩⟩⟩ { Algebra.ofId ℂ A with toFun := algebraMap ℂ A diff --git a/Mathlib/Analysis/Normed/Ring/WithAbs.lean b/Mathlib/Analysis/Normed/Ring/WithAbs.lean index 9dfd6beccf2443..f91c84490faa61 100644 --- a/Mathlib/Analysis/Normed/Ring/WithAbs.lean +++ b/Mathlib/Analysis/Normed/Ring/WithAbs.lean @@ -253,8 +253,7 @@ variable [Semiring T] [Module R T] (v : AbsoluteValue T S) variable (R) in /-- The canonical `R`-linear isomorphism between `WithAbs v` and `T`, when `v : AbsoluteValue T S`. -/ -def linearEquiv [Semiring T] [Module R T] (v : AbsoluteValue T S) : - WithAbs v ≃ₗ[R] T := (equiv v).linearEquiv R +def linearEquiv : WithAbs v ≃ₗ[R] T := (equiv v).linearEquiv R variable {v} diff --git a/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean b/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean index 53cd91e2385c3f..c43ff4f06a2bae 100644 --- a/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean +++ b/Mathlib/Analysis/Normed/Unbundled/SpectralNorm.lean @@ -880,7 +880,7 @@ def normedField : NormedField L := /-- `L` with the spectral norm is a `NontriviallyNormedField`. -/ @[implicit_reducible] -def nontriviallyNormedField [CompleteSpace K] : NontriviallyNormedField L where +def nontriviallyNormedField : NontriviallyNormedField L where __ := spectralNorm.normedField K L non_trivial := let ⟨x, hx⟩ := NontriviallyNormedField.non_trivial (α := K) diff --git a/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean b/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean index aa4dca15fcc895..49c62e0554ec61 100644 --- a/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean +++ b/Mathlib/CategoryTheory/FiberedCategory/Cartesian.lean @@ -188,7 +188,7 @@ lemma universal_property {R' : 𝒮} {a' : 𝒳} (g : R' ⟶ R) (f' : R' ⟶ S) have : p.IsHomLift (g ≫ f) φ' := (hf' ▸ inferInstance) apply IsStronglyCartesian.universal_property' f -instance isCartesian_of_isStronglyCartesian [p.IsStronglyCartesian f φ] : p.IsCartesian f φ where +instance isCartesian_of_isStronglyCartesian : p.IsCartesian f φ where universal_property := fun φ' => universal_property p f φ (𝟙 R) f (by simp) φ' section diff --git a/Mathlib/CategoryTheory/FiberedCategory/Cocartesian.lean b/Mathlib/CategoryTheory/FiberedCategory/Cocartesian.lean index 3e40ddd5580744..3ec9007a50a9b8 100644 --- a/Mathlib/CategoryTheory/FiberedCategory/Cocartesian.lean +++ b/Mathlib/CategoryTheory/FiberedCategory/Cocartesian.lean @@ -177,8 +177,7 @@ lemma universal_property {S' : 𝒮} {b' : 𝒳} (g : S ⟶ S') (f' : R ⟶ S') have : p.IsHomLift (f ≫ g) φ' := (hf' ▸ inferInstance) apply IsStronglyCocartesian.universal_property' f -instance isCocartesian_of_isStronglyCocartesian [p.IsStronglyCocartesian f φ] : - p.IsCocartesian f φ where +instance isCocartesian_of_isStronglyCocartesian : p.IsCocartesian f φ where universal_property := fun φ' => universal_property p f φ (𝟙 S) f (comp_id f).symm φ' section diff --git a/Mathlib/Combinatorics/Matroid/Rank/Cardinal.lean b/Mathlib/Combinatorics/Matroid/Rank/Cardinal.lean index 3d27b1da30689b..ce4ced50f56649 100644 --- a/Mathlib/Combinatorics/Matroid/Rank/Cardinal.lean +++ b/Mathlib/Combinatorics/Matroid/Rank/Cardinal.lean @@ -247,8 +247,7 @@ theorem IsBase.cardinalMk_eq_cRank (hB : M.IsBase B) : #B = M.cRank := by simp [cRank, hrw] /-- Restrictions of matroids with cardinal rank functions have cardinal rank functions. -/ -instance invariantCardinalRank_restrict [InvariantCardinalRank M] : - InvariantCardinalRank (M ↾ X) := by +instance invariantCardinalRank_restrict : InvariantCardinalRank (M ↾ X) := by refine ⟨fun I J Y hI hJ ↦ ?_⟩ rw [isBasis_restrict_iff'] at hI hJ exact hI.1.cardinalMk_diff_comm hJ.1 diff --git a/Mathlib/Data/Finset/SMulAntidiagonal.lean b/Mathlib/Data/Finset/SMulAntidiagonal.lean index 7207af94bf049a..418d1d93bc9a95 100644 --- a/Mathlib/Data/Finset/SMulAntidiagonal.lean +++ b/Mathlib/Data/Finset/SMulAntidiagonal.lean @@ -68,7 +68,7 @@ and `t` are well-ordered. -/ @[to_additive /-- `Finset.VAddAntidiagonal hs ht a` is the set of all pairs of an element in `s` and an element in `t` whose vector addition yields `a`, but its construction requires proofs that `s` and `t` are well-ordered. -/] -noncomputable def SMulAntidiagonal [IsOrderedCancelSMul G P] +noncomputable def SMulAntidiagonal {s : Set G} {t : Set P} (hs : s.IsPWO) (ht : t.IsPWO) (a : P) : Finset (G × P) := (SMulAntidiagonal.finite_of_isPWO hs ht a).toFinset diff --git a/Mathlib/FieldTheory/Finite/Extension.lean b/Mathlib/FieldTheory/Finite/Extension.lean index 58f7f74d502399..64eae53c61b125 100644 --- a/Mathlib/FieldTheory/Finite/Extension.lean +++ b/Mathlib/FieldTheory/Finite/Extension.lean @@ -45,7 +45,7 @@ namespace FiniteField /-- Given a finite field `k` of characteristic `p`, we have a non-canonically chosen extension of any given degree `n > 0`. -/ -def Extension [CharP k p] : Type := +def Extension : Type := letI := ZMod.algebra k p GaloisField p (Module.finrank (ZMod p) k * n) deriving Field, Finite, Algebra (ZMod p), FiniteDimensional (ZMod p) @@ -53,8 +53,9 @@ def Extension [CharP k p] : Type := theorem finrank_zmod_extension [Algebra (ZMod p) k] : Module.finrank (ZMod p) (Extension k p n) = Module.finrank (ZMod p) k * n := by letI := ZMod.algebra k p + unfold Extension convert GaloisField.finrank p (n := Module.finrank (ZMod p) k * n) <| - mul_ne_zero Module.finrank_pos.ne' <| NeZero.ne n using 4 + mul_ne_zero Module.finrank_pos.ne' <| NeZero.ne n subsingleton theorem nonempty_algHom_extension [Algebra (ZMod p) k] : diff --git a/Mathlib/FieldTheory/PurelyInseparable/Exponent.lean b/Mathlib/FieldTheory/PurelyInseparable/Exponent.lean index c1392acce96683..0f59b9d70f30b5 100644 --- a/Mathlib/FieldTheory/PurelyInseparable/Exponent.lean +++ b/Mathlib/FieldTheory/PurelyInseparable/Exponent.lean @@ -214,7 +214,7 @@ theorem elemExponent_le_exponent [HasExponent K L] (a : L) : elemExponent_le_of_pow_mem <| exponent_def K a variable {K} in -instance hasExponent_of_finiteDimensional [IsPurelyInseparable K L] [FiniteDimensional K L] : +instance hasExponent_of_finiteDimensional [FiniteDimensional K L] : HasExponent K L := by let ⟨p, _⟩ := ExpChar.exists K rcases ‹ExpChar K p› with _ | ⟨hp⟩ diff --git a/Mathlib/NumberTheory/NumberField/Units/Basic.lean b/Mathlib/NumberTheory/NumberField/Units/Basic.lean index f0a8c93b042dc8..2a16846f0e3ff0 100644 --- a/Mathlib/NumberTheory/NumberField/Units/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/Units/Basic.lean @@ -168,9 +168,9 @@ instance : Fintype (torsion K) := by instance : IsCyclic (torsion K) := isCyclic_subgroup_units _ /-- The order of the torsion subgroup. -/ -def torsionOrder [NumberField K] : ℕ := Fintype.card (torsion K) +def torsionOrder : ℕ := Fintype.card (torsion K) -instance [NumberField K] : NeZero (torsionOrder K) := +instance : NeZero (torsionOrder K) := inferInstanceAs (NeZero (Fintype.card (torsion K))) theorem torsionOrder_ne_zero : diff --git a/Mathlib/Order/CompleteLattice/PiLex.lean b/Mathlib/Order/CompleteLattice/PiLex.lean index 4a6aaaf3a90446..a19760b411479a 100644 --- a/Mathlib/Order/CompleteLattice/PiLex.lean +++ b/Mathlib/Order/CompleteLattice/PiLex.lean @@ -25,12 +25,13 @@ namespace Pi /-! ### Lexicographic ordering -/ namespace Lex -variable [WellFoundedLT ι] private def inf [WellFoundedLT ι] (s : Set (Πₗ i, α i)) (i : ι) : α i := ⨅ e : {e ∈ s | ∀ j < i, e j = inf s j}, e.1 i termination_by wellFounded_lt.wrap i +variable [WellFoundedLT ι] + @[no_expose] instance : InfSet (Πₗ i, α i) where sInf s := toLex (inf s) diff --git a/Mathlib/Order/CompletePartialOrder.lean b/Mathlib/Order/CompletePartialOrder.lean index c9071eb0c205a3..8c66ab6adad426 100644 --- a/Mathlib/Order/CompletePartialOrder.lean +++ b/Mathlib/Order/CompletePartialOrder.lean @@ -98,7 +98,7 @@ instance (priority := 100) CompletePartialOrder.toOmegaCompletePartialOrder : ωSup_le c _ := c.directed.iSup_le /-- A complete partial order is an conditionally complete partial order. -/ -instance (priority := 100) [CompletePartialOrder α] : ConditionallyCompletePartialOrderSup α where +instance (priority := 100) : ConditionallyCompletePartialOrderSup α where isLUB_csSup_of_directed _ h_dir _ _ := h_dir.isLUB_sSup end CompletePartialOrder diff --git a/Mathlib/Probability/Independence/Process/HasIndepIncrements/IsGaussianProcess.lean b/Mathlib/Probability/Independence/Process/HasIndepIncrements/IsGaussianProcess.lean index bc2d33be6bf65c..83a680adb52220 100644 --- a/Mathlib/Probability/Independence/Process/HasIndepIncrements/IsGaussianProcess.lean +++ b/Mathlib/Probability/Independence/Process/HasIndepIncrements/IsGaussianProcess.lean @@ -88,7 +88,7 @@ namespace ProbabilityTheory /-- `incrementsToRestrict I` is a continuous linear map `f` such that if `t₁ < ... < tₙ` are then elements of `I`, then `f (xₜ₁, xₜ₂ - xₜ₁, ..., xₜₙ - xₜₙ₋₁) = (xₜ₁, ..., xₜₙ)`. -/ -noncomputable def incrementsToRestrict [LinearOrder T] (R : Type*) [Semiring R] [AddCommMonoid E] +noncomputable def incrementsToRestrict (R : Type*) [Semiring R] [AddCommMonoid E] [Module R E] [TopologicalSpace E] [ContinuousAdd E] (I : Finset T) : (Fin #I → E) →L[R] (I → E) := { toFun x i := ∑ j ≤ (I.orderIsoOfFin rfl).symm i, x j diff --git a/Mathlib/RingTheory/Extension/Presentation/Submersive.lean b/Mathlib/RingTheory/Extension/Presentation/Submersive.lean index bc9da3cb16055e..2fa60369575d4d 100644 --- a/Mathlib/RingTheory/Extension/Presentation/Submersive.lean +++ b/Mathlib/RingTheory/Extension/Presentation/Submersive.lean @@ -618,7 +618,7 @@ noncomputable def aevalDifferentialEquiv (P : SubmersivePresentation R S ι σ) variable (P : SubmersivePresentation R S ι σ) @[simp] -lemma aevalDifferentialEquiv_apply [Finite σ] (x : σ → S) : +lemma aevalDifferentialEquiv_apply (x : σ → S) : P.aevalDifferentialEquiv x = P.aevalDifferential x := rfl diff --git a/Mathlib/RingTheory/Ideal/KrullsHeightTheorem.lean b/Mathlib/RingTheory/Ideal/KrullsHeightTheorem.lean index 39653fae2dc613..a48a50b95c7553 100644 --- a/Mathlib/RingTheory/Ideal/KrullsHeightTheorem.lean +++ b/Mathlib/RingTheory/Ideal/KrullsHeightTheorem.lean @@ -260,7 +260,7 @@ instance Ideal.finiteHeight_of_isNoetherianRing (I : Ideal R) : I.FiniteHeight := finiteHeight_iff_lt.mpr <| Or.elim (em (I = ⊤)) Or.inl fun h ↦ Or.inr <| (I.height_le_spanFinrank h).trans_lt (ENat.coe_lt_top _) -instance [IsNoetherianRing R] [IsLocalRing R] : FiniteRingKrullDim R := by +instance [IsLocalRing R] : FiniteRingKrullDim R := by apply finiteRingKrullDim_iff_ne_bot_and_top.mpr rw [← IsLocalRing.maximalIdeal_height_eq_ringKrullDim] constructor diff --git a/Mathlib/RingTheory/MvPowerSeries/Rename.lean b/Mathlib/RingTheory/MvPowerSeries/Rename.lean index cda1ea4f2e1233..1f42baed4e89fb 100644 --- a/Mathlib/RingTheory/MvPowerSeries/Rename.lean +++ b/Mathlib/RingTheory/MvPowerSeries/Rename.lean @@ -57,7 +57,7 @@ section Semiring variable [Semiring R] [Semiring S] /-- Implementation detail for `rename`. Use `MvPowerSeries.rename` instead. -/ -def renameFun [TendstoCofinite f] : MvPowerSeries σ R → MvPowerSeries τ R := +def renameFun : MvPowerSeries σ R → MvPowerSeries τ R := TendstoCofinite.mapDomain (Finsupp.mapDomain f) private lemma coeff_renameFun {p : MvPowerSeries σ R} {x : τ →₀ ℕ} : (renameFun f p).coeff x = @@ -115,7 +115,7 @@ variable [CommSemiring R] [CommSemiring S] /-- Rename all the variables in a multivariable power series by a map with finite fibers. -/ @[no_expose] -def rename [TendstoCofinite f] : MvPowerSeries σ R →ₐ[R] MvPowerSeries τ R where +def rename : MvPowerSeries σ R →ₐ[R] MvPowerSeries τ R where toFun := renameFun f map_one' := renameFun_monomial f 0 1 map_mul' := renameFun_mul f diff --git a/Mathlib/RingTheory/SimpleModule/WedderburnArtin.lean b/Mathlib/RingTheory/SimpleModule/WedderburnArtin.lean index 2d11c54d7fa911..990ada06d27c19 100644 --- a/Mathlib/RingTheory/SimpleModule/WedderburnArtin.lean +++ b/Mathlib/RingTheory/SimpleModule/WedderburnArtin.lean @@ -234,7 +234,7 @@ instance (n) [Fintype n] [DecidableEq n] : IsSemisimpleRing (Matrix n n R) := have ⟨_, _, _, _, _, ⟨e⟩⟩ := exists_ringEquiv_pi_matrix_divisionRing R (e.mapMatrix (m := n).trans Matrix.piRingEquiv).symm.isSemisimpleRing -instance [IsSemisimpleRing R] : IsSemisimpleRing Rᵐᵒᵖ := +instance : IsSemisimpleRing Rᵐᵒᵖ := have ⟨_, _, _, _, _, ⟨e⟩⟩ := exists_ringEquiv_pi_matrix_divisionRing R ((e.op.trans (.piMulOpposite _)).trans (.piCongrRight fun _ ↦ .symm .mopMatrix)).symm |>.isSemisimpleRing diff --git a/Mathlib/RingTheory/Smooth/StandardSmoothCotangent.lean b/Mathlib/RingTheory/Smooth/StandardSmoothCotangent.lean index 5994e51949b2b2..826a96cb9a3f91 100644 --- a/Mathlib/RingTheory/Smooth/StandardSmoothCotangent.lean +++ b/Mathlib/RingTheory/Smooth/StandardSmoothCotangent.lean @@ -76,7 +76,7 @@ namespace SubmersivePresentation variable [Finite σ] (P : SubmersivePresentation R S ι σ) set_option backward.isDefEq.respectTransparency false in -lemma cotangentComplexAux_injective [Finite σ] : Function.Injective P.cotangentComplexAux := by +lemma cotangentComplexAux_injective : Function.Injective P.cotangentComplexAux := by rw [← LinearMap.ker_eq_bot, eq_bot_iff] intro x hx obtain ⟨(x : P.ker), rfl⟩ := Cotangent.mk_surjective x @@ -113,7 +113,7 @@ lemma cotangentComplexAux_injective [Finite σ] : Function.Injective P.cotangent simpa using this i · exact P.relation_mem_ker i -lemma cotangentComplexAux_surjective [Finite σ] : Function.Surjective P.cotangentComplexAux := by +lemma cotangentComplexAux_surjective : Function.Surjective P.cotangentComplexAux := by rw [← LinearMap.range_eq_top, _root_.eq_top_iff, ← P.basisDeriv.span_eq, Submodule.span_le] rintro - ⟨i, rfl⟩ use Cotangent.mk ⟨P.relation i, P.relation_mem_ker i⟩ @@ -166,8 +166,7 @@ noncomputable def sectionCotangent : P.toExtension.CotangentSpace →ₗ[S] P.to (cotangentEquiv P).symm ∘ₗ (Finsupp.linearEquivFunOnFinite S S σ).toLinearMap ∘ₗ Finsupp.lcomapDomain _ P.map_inj ∘ₗ P.cotangentSpaceBasis.repr.toLinearMap -lemma sectionCotangent_eq_iff [Finite σ] - (x : P.toExtension.CotangentSpace) (y : P.toExtension.Cotangent) : +lemma sectionCotangent_eq_iff (x : P.toExtension.CotangentSpace) (y : P.toExtension.Cotangent) : sectionCotangent P x = y ↔ ∀ i : σ, P.cotangentSpaceBasis.repr x (P.map i) = (P.cotangentComplexAux y) i := by simp only [sectionCotangent, LinearMap.coe_comp, LinearEquiv.coe_coe, Function.comp_apply] diff --git a/Mathlib/Topology/Algebra/Module/Alternating/Topology.lean b/Mathlib/Topology/Algebra/Module/Alternating/Topology.lean index bd127dc8ccfc85..4a0a71755f5d15 100644 --- a/Mathlib/Topology/Algebra/Module/Alternating/Topology.lean +++ b/Mathlib/Topology/Algebra/Module/Alternating/Topology.lean @@ -247,7 +247,6 @@ lemma liftCLM_apply (f : G →L[𝕜] ContinuousMultilinearMap 𝕜 (fun _ : ι section CompContinuousLinearMap variable {E' : Type*} [AddCommGroup E'] [Module 𝕜 E'] [TopologicalSpace E'] - [ContinuousConstSMul 𝕜 F] /-- Composition of a continuous alternating map and a continuous linear map as a bundled continuous linear map. diff --git a/Mathlib/Topology/Algebra/Module/Spaces/PointwiseConvergenceCLM.lean b/Mathlib/Topology/Algebra/Module/Spaces/PointwiseConvergenceCLM.lean index 1d9a5b06a88755..f40275189156a6 100644 --- a/Mathlib/Topology/Algebra/Module/Spaces/PointwiseConvergenceCLM.lean +++ b/Mathlib/Topology/Algebra/Module/Spaces/PointwiseConvergenceCLM.lean @@ -132,8 +132,7 @@ variable (G) in /-- Pre-composition by a *fixed* continuous linear map as a continuous linear map for the pointwise convergence topology. -/ @[simps! apply] -def precomp [IsTopologicalAddGroup G] [ContinuousConstSMul 𝕜₃ G] (L : E →SL[σ] F) : - (F →SLₚₜ[τ] G) →L[𝕜₃] E →SLₚₜ[ρ] G where +def precomp [ContinuousConstSMul 𝕜₃ G] (L : E →SL[σ] F) : (F →SLₚₜ[τ] G) →L[𝕜₃] E →SLₚₜ[ρ] G where toFun f := f.comp L __ := ContinuousLinearMap.precompUniformConvergenceCLM G {(S : Set E) | Finite S} {(S : Set F) | Finite S} L (fun S hS ↦ letI : Finite S := hS; Finite.Set.finite_image _ _) diff --git a/Mathlib/Topology/Algebra/Valued/WithVal.lean b/Mathlib/Topology/Algebra/Valued/WithVal.lean index 443a44155182d8..ed0d0716438759 100644 --- a/Mathlib/Topology/Algebra/Valued/WithVal.lean +++ b/Mathlib/Topology/Algebra/Valued/WithVal.lean @@ -217,7 +217,7 @@ instance [SMul S R] [FaithfulSMul S R] : FaithfulSMul S (WithVal v) where simp only [smul_right_def, toVal.injEq] at h exact FaithfulSMul.eq_of_smul_eq_smul fun r ↦ h (toVal v r) -instance {P : Type*} [Ring R] [SMul S P] [SMul R S] [SMul R P] +instance {P : Type*} [SMul S P] [SMul R S] [SMul R P] [IsScalarTower R S P] (v : Valuation R Γ₀) : IsScalarTower (WithVal v) S P where smul_assoc := by simp [smul_left_def] @@ -253,7 +253,7 @@ def linearEquiv : WithVal v ≃ₗ[R] S := (equiv v).linearEquiv R @[simp] theorem linearEquiv_symm_apply (x : S) : (linearEquiv R v).symm x = toVal v x := rfl -instance [Module R S] [Module.Finite R S] : +instance [Module.Finite R S] : Module.Finite R (WithVal v) := .equiv (linearEquiv R v).symm end Module diff --git a/Mathlib/Topology/Convenient/GeneratedBy.lean b/Mathlib/Topology/Convenient/GeneratedBy.lean index a31d15ac479364..e2acde780c88ad 100644 --- a/Mathlib/Topology/Convenient/GeneratedBy.lean +++ b/Mathlib/Topology/Convenient/GeneratedBy.lean @@ -151,7 +151,7 @@ section variable [IsGeneratedBy X Y] /-- The homeomorphism `WithGeneratedByTopology X Y ≃ₜ Y` when `Y` is `X`-generated. -/ -def homeomorph [IsGeneratedBy X Y] : WithGeneratedByTopology X Y ≃ₜ Y where +def homeomorph : WithGeneratedByTopology X Y ≃ₜ Y where toEquiv := WithGeneratedByTopology.equiv continuous_toFun := by dsimp; fun_prop continuous_invFun := by dsimp; fun_prop diff --git a/Mathlib/Topology/Sion.lean b/Mathlib/Topology/Sion.lean index 0e271efdcdaa49..1b8b6abc3f55bd 100644 --- a/Mathlib/Topology/Sion.lean +++ b/Mathlib/Topology/Sion.lean @@ -175,8 +175,7 @@ variable [TopologicalSpace F] [AddCommGroup F] [Module ℝ F] variable (X Y f) in /-- The set of parameters `z` in the segment `[y, y']` such that `f b z ≤ f b' y`. -/ -def setOf_sublevelLeft_subset [AddCommGroup F] [Module ℝ F] - (b b' : β) (y y' : Y) : Set (segment ℝ y.val y'.val) := +def setOf_sublevelLeft_subset (b b' : β) (y y' : Y) : Set (segment ℝ y.val y'.val) := {z | sublevelLeft X f b z ⊆ sublevelLeft X f b' y} include ne_X kX hfx hfx' cY hfy hfy' in diff --git a/Mathlib/Topology/VectorBundle/Basic.lean b/Mathlib/Topology/VectorBundle/Basic.lean index d3d00d89fac25b..2485b82762a164 100644 --- a/Mathlib/Topology/VectorBundle/Basic.lean +++ b/Mathlib/Topology/VectorBundle/Basic.lean @@ -504,7 +504,7 @@ theorem comp_continuousLinearEquivAt_eq_coord_change (e e' : Trivialization F ( end Bundle.Trivialization -variable (F E) [TopologicalSpace (TotalSpace F E)] [FiberBundle F E] [VectorBundle R F E] in +variable (F E) [VectorBundle R F E] in /-- A continuous linear equivalence between the fiber at `b` and the model fiber, induced by the preferred trivialisation at each `b`. -/ @[simps!] From d0d5a9ceded0d0c93c37e8e06248a5d93de30cd1 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 16 Apr 2026 22:37:04 +0200 Subject: [PATCH 087/141] improve output message --- .../Tactic/Linter/OverlappingInstances.lean | 27 +++++---- MathlibTest/OverlappingInstances.lean | 58 +++++++++---------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index cf6858079c41a5..550ed312e1acfb 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -124,7 +124,7 @@ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId let projClasses ← forallTelescopeReducing (whnfType := true) type fun xs type ↦ do type.withApp fun f args ↦ do let .const cls us := f | - throwError "`{decl.toExpr}` has an instance implicit binder, but it is not an instance" + return #[] -- This happens when using `set_option checkBinderAnnotations false` let info ← getConstInfo cls let projs ← getAbstractDataProjectionsCached cls projs.mapM fun proj ↦ @@ -146,20 +146,25 @@ register_option linter.overlappingInstances : Bool := { /-- Creates a message describing the violations captured in `Overlaps`, assumed to be nonempty. -/ def overlapsToMsg (overlaps : Std.HashMap Expr (Array FVarId)) (ctx : ContextInfo) : MetaM MessageData := do + let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := + overlaps.fold (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) + let mut msgs := #[] + for (fvars, overlaps) in sortedOverlaps do + let parents ← fvars.mapM (do instantiateMVars <| ← ·.getType) + if parents.all (· == parents[0]!) then + msgs := msgs.push <| m!"There are {parents.size} `{.sbracket parents[0]!}` instances" + else + let parents := parents.map (m!"`{.sbracket ·}`") + let children := overlaps.map fun overlap => m!"`{overlap}`" + msgs := msgs.push <| + m!"{.andList parents.toList} give conflicting instances of {.andList children.toList}." + -- Create a bulleted list if there are multiple messages, otherwise just a single line let declDescr ← if let some decl := ctx.parentDecl? then - pure m!"Declaration `{.ofConstName (← unresolveNameGlobal decl)}`" + pure m!"Declaration `{← unresolveNameGlobal decl}`" else pure "The current declaration" - let mut msg := m!"{declDescr} \ - has instance hypotheses which provide conflicting versions of the same data. Specifically:" - let mut msgs := #[] - for (overlap, fvars) in overlaps do - let parents ← fvars.mapM fun fvarId ↦ return m!"`{.sbracket (← fvarId.getType)}`" - -- let overlap := MessageData.ofInstanceBinderType overlap - msgs := msgs.push <| - m!"{.andList parents.toList} provide conflicting instances of `{overlap}`." - -- Create a bulleted list if there are multiple messages, otherwise just a single line + let mut msg := m!"{declDescr} has overlapping instances:" msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index e581deb9cfd7de..1c5664002a5441 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -30,9 +30,9 @@ error: unsolved goals inst✝¹ inst✝ : Add Nat ⊢ [Add Nat] → [Add Nat] → Bool --- -warning: Declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo` has overlapping instances: -`[Add Nat]`, `[Add Nat]`, `[Add Nat]`, and `[Add Nat]` provide conflicting instances of `Add Nat`. +There are 4 `[Add Nat]` instances Consider choosing different instance hypotheses. @@ -45,9 +45,9 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- @ +3:68...+4:12 -warning: Declaration `foo₁` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` provide conflicting instances of `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. Consider choosing different instance hypotheses. @@ -60,10 +60,10 @@ set_option linter.overlappingInstances true in exact true /-- -warning: Declaration `foo₂` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₂` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` provide conflicting instances of `SubBar Nat`. +• There are 2 `[FooBarBaz Nat]` instances +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. Consider choosing different instance hypotheses. @@ -73,10 +73,9 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- -warning: Declaration `foo₃` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₃` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. -• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `SubBar Nat`. +There are 2 `[FooBarBaz Nat]` instances Consider choosing different instance hypotheses. @@ -86,10 +85,10 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- -warning: Declaration `foo₄` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₄` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz Nat]` provide conflicting instances of `Baz Nat`. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` provide conflicting instances of `SubBar Nat`. +• There are 2 `[FooBarBaz Nat]` instances +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `SubBar Nat`. Consider choosing different instance hypotheses. @@ -100,10 +99,9 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial -- Note that `[SubBar Nat]` is absent, as `[Bar Nat]` is already reported. /-- -warning: Declaration `foo₅` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo₅` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` provide conflicting instances of `Baz Nat`. -• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` provide conflicting instances of `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `Baz Nat` and `SubBar Nat`. Consider choosing different instance hypotheses. @@ -117,9 +115,9 @@ namespace Foo /-! Test unresolving name (`foo`, not `Foo.foo` or `_private...foo`) -/ /-- -warning: Declaration `foo` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `foo` has overlapping instances: -`[Add Nat]` and `[Add Nat]` provide conflicting instances of `Add Nat`. +There are 2 `[Add Nat]` instances Consider choosing different instance hypotheses. @@ -139,9 +137,9 @@ class inductive IndFoo where | mk₁ (n : Nat) | mk₂ (b : Bool) /-- -warning: Declaration `indFoo` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `indFoo` has overlapping instances: -`[IndFoo]` and `[IndFoo]` provide conflicting instances of `IndFoo`. +There are 2 `[IndFoo]` instances Consider choosing different instance hypotheses. @@ -155,9 +153,9 @@ class inductive IndFooProp : Prop where -- We also warn when there are duplicate `Prop` clases /-- -warning: Declaration `indFooProp` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `indFooProp` has overlapping instances: -`[IndFooProp]` and `[IndFooProp]` provide conflicting instances of `IndFooProp`. +There are 2 `[IndFooProp]` instances Consider choosing different instance hypotheses. @@ -173,9 +171,9 @@ section instantiateMVars variable {α : Type*} [Repr α] /-- -warning: Declaration `needsInstantiateMVars` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `needsInstantiateMVars` has overlapping instances: -`[Repr α]` and `[Repr α]` provide conflicting instances of `Repr α`. +There are 2 `[Repr α]` instances Consider choosing different instance hypotheses. @@ -194,9 +192,9 @@ def fooNothing [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := true set_option linter.overlappingInstances false /-- -warning: Declaration `fooSomething` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `fooSomething` has overlapping instances: -`[Add Nat]`, `[Add Nat]`, `[Add Nat]`, and `[Add Nat]` provide conflicting instances of `Add Nat`. +There are 4 `[Add Nat]` instances Consider choosing different instance hypotheses. @@ -215,9 +213,9 @@ class A (α : Sort u) where class B (α : Type u) extends A α /-- -warning: Declaration `_example` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` provide conflicting instances of `A α`. +`[B α]` and `[A α]` give conflicting instances of `A α`. Consider choosing different instance hypotheses. @@ -239,9 +237,9 @@ class B (α β : Type*) [A α] where class B' (α β : Type*) [A' α] extends B α β where /-- -warning: Declaration `_example` has instance hypotheses which provide conflicting versions of the same data. Specifically: +warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` provide conflicting instances of `B α β`. +`[B α β]` and `[B' α β]` give conflicting instances of `B α β`. Consider choosing different instance hypotheses. From db46d604ced9d17466f88ad65e103af27e2f024f Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 17 Apr 2026 01:38:03 +0200 Subject: [PATCH 088/141] fix all? --- Mathlib/Algebra/Algebra/Shrink.lean | 2 +- Mathlib/Algebra/Group/Action/Defs.lean | 4 +- .../Algebra/Group/Submonoid/Operations.lean | 3 +- .../Homology/DerivedCategory/Ext/Map.lean | 4 +- .../Algebra/Homology/HomologySequence.lean | 3 +- Mathlib/Algebra/Module/Equiv/Defs.lean | 1 + Mathlib/Algebra/MonoidAlgebra/Defs.lean | 2 +- Mathlib/Algebra/Order/Star/Basic.lean | 1 - Mathlib/AlgebraicGeometry/Modules/Sheaf.lean | 22 +++--- .../ModelCategory/IsCofibrant.lean | 11 +-- .../CategoryTheory/Adhesive/Subobject.lean | 2 +- .../Limits/Shapes/ZeroMorphisms.lean | 2 +- .../Localization/Predicate.lean | 1 + .../Monoidal/Closed/Cartesian.lean | 1 + Mathlib/CategoryTheory/Monoidal/Functor.lean | 2 +- Mathlib/CategoryTheory/Monoidal/Grp_.lean | 3 +- .../Preadditive/AdditiveFunctor.lean | 4 +- Mathlib/CategoryTheory/Shift/Adjunction.lean | 2 - Mathlib/CategoryTheory/Shift/Pullback.lean | 5 +- .../CategoryTheory/Sites/CoverLifting.lean | 2 +- .../Sites/DenseSubsite/Basic.lean | 6 +- .../CategoryTheory/Sites/Hypercover/One.lean | 3 +- .../Sites/Point/OfIsCofiltered.lean | 2 +- .../Sites/Point/Skyscraper.lean | 2 +- Mathlib/CategoryTheory/Sites/Pullback.lean | 2 +- .../CategoryTheory/Triangulated/Functor.lean | 2 +- Mathlib/Control/Applicative.lean | 1 + Mathlib/Control/Functor.lean | 1 + Mathlib/GroupTheory/GroupAction/Hom.lean | 78 ++++++------------- .../GroupTheory/OreLocalization/Basic.lean | 1 + Mathlib/LinearAlgebra/FreeModule/Basic.lean | 6 +- .../Matrix/SpecialLinearGroup.lean | 2 +- Mathlib/Logic/Basic.lean | 1 + Mathlib/Order/MinMax.lean | 2 +- Mathlib/RepresentationTheory/Rep/Res.lean | 3 +- .../AdicCompletion/AsTensorProduct.lean | 2 +- Mathlib/RingTheory/Algebraic/Integral.lean | 4 - .../DedekindDomain/Ideal/Basic.lean | 2 +- Mathlib/RingTheory/GradedAlgebra/Basic.lean | 4 +- .../Localization/AtPrime/Basic.lean | 3 +- .../Localization/AtPrime/Extension.lean | 2 +- Mathlib/RingTheory/Morita/Matrix.lean | 4 +- Mathlib/Tactic/Translate/Reorder.lean | 2 +- Mathlib/Topology/Algebra/MulAction.lean | 1 + .../Compactness/CompactlyGeneratedSpace.lean | 2 +- 45 files changed, 86 insertions(+), 129 deletions(-) diff --git a/Mathlib/Algebra/Algebra/Shrink.lean b/Mathlib/Algebra/Algebra/Shrink.lean index b5cf5dbf453fdf..9e4420f5161236 100644 --- a/Mathlib/Algebra/Algebra/Shrink.lean +++ b/Mathlib/Algebra/Algebra/Shrink.lean @@ -26,7 +26,7 @@ instance [Semiring α] [Algebra R α] : Algebra R (Shrink.{v} α) := (equivShrin variable (R α) in /-- Shrinking `α` to a smaller universe preserves algebra structure. -/ @[simps!] -def algEquiv [Small.{v} α] [Semiring α] [Algebra R α] : Shrink.{v} α ≃ₐ[R] α := +def algEquiv [Semiring α] [Algebra R α] : Shrink.{v} α ≃ₐ[R] α := (equivShrink α).symm.algEquiv _ end Shrink diff --git a/Mathlib/Algebra/Group/Action/Defs.lean b/Mathlib/Algebra/Group/Action/Defs.lean index 57bc2b7910a181..61586061929264 100644 --- a/Mathlib/Algebra/Group/Action/Defs.lean +++ b/Mathlib/Algebra/Group/Action/Defs.lean @@ -619,10 +619,10 @@ but here you can use the even stronger class `MulSemiringAction`, which captures how the action plays with both multiplication and addition. -/ @[ext] class MulDistribMulAction (M N : Type*) [Monoid M] [Monoid N] extends MulAction M N where - /-- Distributivity of `•` across `*` -/ - smul_mul : ∀ (r : M) (x y : N), r • (x * y) = r • x * r • y /-- Multiplying `1` by a scalar gives `1` -/ smul_one : ∀ r : M, r • (1 : N) = 1 + /-- Distributivity of `•` across `*` -/ + smul_mul : ∀ (r : M) (x y : N), r • (x * y) = r • x * r • y export MulDistribMulAction (smul_one) diff --git a/Mathlib/Algebra/Group/Submonoid/Operations.lean b/Mathlib/Algebra/Group/Submonoid/Operations.lean index 9307ed2231ced6..0a2372b8673d28 100644 --- a/Mathlib/Algebra/Group/Submonoid/Operations.lean +++ b/Mathlib/Algebra/Group/Submonoid/Operations.lean @@ -1119,7 +1119,8 @@ namespace Submonoid elements of `M`. -/ @[to_additive (attr := simps!) /-- The additive equivalence between the type of additive units of `M` and the additive submonoid whose elements are the additive units of `M`. -/] -noncomputable def unitsTypeEquivIsUnitSubmonoid [Monoid M] : Mˣ ≃* IsUnit.submonoid M where +noncomputable def unitsTypeEquivIsUnitSubmonoid {M : Type*} [Monoid M] : + Mˣ ≃* IsUnit.submonoid M where toFun x := ⟨x, Units.isUnit x⟩ invFun x := x.prop.unit left_inv _ := IsUnit.unit_of_val_units _ diff --git a/Mathlib/Algebra/Homology/DerivedCategory/Ext/Map.lean b/Mathlib/Algebra/Homology/DerivedCategory/Ext/Map.lean index 2682334fdac0a3..6226ef0f88c6c2 100644 --- a/Mathlib/Algebra/Homology/DerivedCategory/Ext/Map.lean +++ b/Mathlib/Algebra/Homology/DerivedCategory/Ext/Map.lean @@ -166,7 +166,7 @@ lemma Abelian.Ext.mapExactFunctor_add (f g : Ext.{w} X Y n) : aesop /-- Upgraded of `CategoryTheory.Abelian.Ext.mapExactFunctor` into an additive homomorphism. -/ -noncomputable def Functor.mapExtAddHom [HasExt.{w} C] [HasExt.{w'} D] (X Y : C) (n : ℕ) : +noncomputable def Functor.mapExtAddHom (X Y : C) (n : ℕ) : Ext.{w} X Y n →+ Ext.{w'} (F.obj X) (F.obj Y) n where toFun e := e.mapExactFunctor F map_zero' := by simp @@ -186,7 +186,7 @@ lemma Functor.mapExactFunctor_smul (r : R) (f : Ext.{w} X Y n) : aesop /-- Upgrade of `F.mapExtAddHom` assuming `F` is linear. -/ -noncomputable def Functor.mapExtLinearMap [HasExt.{w} C] [HasExt.{w'} D] (X Y : C) (n : ℕ) : +noncomputable def Functor.mapExtLinearMap (X Y : C) (n : ℕ) : Ext.{w} X Y n →ₗ[R] Ext.{w'} (F.obj X) (F.obj Y) n where __ := F.mapExtAddHom X Y n map_smul' := by simp diff --git a/Mathlib/Algebra/Homology/HomologySequence.lean b/Mathlib/Algebra/Homology/HomologySequence.lean index 62f02c8c68efbe..32dff502772846 100644 --- a/Mathlib/Algebra/Homology/HomologySequence.lean +++ b/Mathlib/Algebra/Homology/HomologySequence.lean @@ -44,8 +44,7 @@ variable {C ι : Type*} [Category* C] [HasZeroMorphisms C] {c : ComplexShape ι} [K.HasHomology i] [K.HasHomology j] [L.HasHomology i] [L.HasHomology j] /-- The morphism `K.opcycles i ⟶ K.cycles j` that is induced by `K.d i j`. -/ -noncomputable def opcyclesToCycles [K.HasHomology i] [K.HasHomology j] : - K.opcycles i ⟶ K.cycles j := +noncomputable def opcyclesToCycles : K.opcycles i ⟶ K.cycles j := K.liftCycles (K.fromOpcycles i j) _ rfl (by simp) @[reassoc (attr := simp)] diff --git a/Mathlib/Algebra/Module/Equiv/Defs.lean b/Mathlib/Algebra/Module/Equiv/Defs.lean index ba58bb7386ab04..8b314f6980343a 100644 --- a/Mathlib/Algebra/Module/Equiv/Defs.lean +++ b/Mathlib/Algebra/Module/Equiv/Defs.lean @@ -301,6 +301,7 @@ variable [RingHomCompTriple σ₁₃ σ₃₄ σ₁₄] [RingHomCompTriple σ₄ variable [RingHomCompTriple σ₂₃ σ₃₄ σ₂₄] [RingHomCompTriple σ₄₃ σ₃₂ σ₄₂] variable (e₁₂ : M₁ ≃ₛₗ[σ₁₂] M₂) (e₂₃ : M₂ ≃ₛₗ[σ₂₃] M₃) +set_option linter.overlappingInstances false in /-- Linear equivalences are transitive. -/ -- Note: the `RingHomCompTriple σ₃₂ σ₂₁ σ₃₁` is unused, but is convenient to carry around -- implicitly for lemmas like `LinearEquiv.self_trans_symm`. diff --git a/Mathlib/Algebra/MonoidAlgebra/Defs.lean b/Mathlib/Algebra/MonoidAlgebra/Defs.lean index 937a70cab75586..312454214b79c9 100644 --- a/Mathlib/Algebra/MonoidAlgebra/Defs.lean +++ b/Mathlib/Algebra/MonoidAlgebra/Defs.lean @@ -273,7 +273,7 @@ example : (smulZeroClass (A := ℕ) (R := R) (M := M)).toSMul = addCommMonoid.to -- Ensure that smul has good defeq properties private local instance {α} [Monoid M] [SMul M α] : SMul Mˣ α where smul m a := (m : M) • a -example [Monoid A] [SMulZeroClass A R] (a : Units A) (x : R[M]) : +example [Monoid A] (a : Units A) (x : R[M]) : a • x = (a : A) • x := by with_reducible_and_instances rfl end diff --git a/Mathlib/Algebra/Order/Star/Basic.lean b/Mathlib/Algebra/Order/Star/Basic.lean index c4aa1393caa0d8..89292e341941cc 100644 --- a/Mathlib/Algebra/Order/Star/Basic.lean +++ b/Mathlib/Algebra/Order/Star/Basic.lean @@ -446,7 +446,6 @@ lemma NonUnitalStarRingHom.map_le_map_of_map_star (f : R →⋆ₙ+* S) {x y : R all_goals aesop instance (priority := 100) StarRingHomClass.instOrderHomClass [FunLike F R S] - [StarOrderedRing R] [StarOrderedRing S] [NonUnitalRingHomClass F R S] [NonUnitalStarRingHomClass F R S] : OrderHomClass F R S where map_rel f := (f : R →⋆ₙ+* S).map_le_map_of_map_star diff --git a/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean b/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean index 8361d3a6527fc2..5d6ce26d285d92 100644 --- a/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean +++ b/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean @@ -30,7 +30,7 @@ open CategoryTheory Limits TopologicalSpace SheafOfModules Bicategory namespace AlgebraicGeometry.Scheme -variable {X Y Z T : Scheme.{u}} (f : X ⟶ Y) (g : Y ⟶ Z) +variable {X Y Z T : Scheme.{u}} variable (X) in /-- The category of sheaves of modules over a scheme. -/ @@ -311,13 +311,12 @@ end Functorial noncomputable section Restriction -variable [IsOpenImmersion f] +variable (f : X ⟶ Y) (g : Y ⟶ Z) [IsOpenImmersion f] [IsOpenImmersion g] /-- Restriction of an `𝒪ₓ`-module along an open immersion. This is isomorphic to the pullback functor (see `restrictFunctorIsoPullback`) but has better defeqs. -/ -def restrictFunctor : - Y.Modules ⥤ X.Modules := +def restrictFunctor : Y.Modules ⥤ X.Modules := letI α : X.presheaf ⟶ f.opensFunctor.op ⋙ Y.presheaf := { app U := (f.appIso U.unop).inv } SheafOfModules.pushforward (F := f.opensFunctor) ⟨Functor.whiskerRight α (forget₂ CommRingCat RingCat)⟩ @@ -390,8 +389,7 @@ lemma restrictFunctorId_inv_app_app : M.presheaf.map (eqToHom (show 𝟙 X ''ᵁ U = U by simp)).op := rfl /-- Restriction along the composition is isomorphic to the composition of restrictions. -/ -def restrictFunctorComp [IsOpenImmersion f] [IsOpenImmersion g] : - restrictFunctor (f ≫ g) ≅ restrictFunctor g ⋙ restrictFunctor f := +def restrictFunctorComp : restrictFunctor (f ≫ g) ≅ restrictFunctor g ⋙ restrictFunctor f := have : (f.opensFunctor ⋙ g.opensFunctor).IsContinuous (Opens.grothendieckTopology X) (Opens.grothendieckTopology Z) := Functor.isContinuous_comp _ _ _ (Opens.grothendieckTopology _) _ @@ -400,11 +398,11 @@ def restrictFunctorComp [IsOpenImmersion f] [IsOpenImmersion g] : (SheafOfModules.pushforwardComp _ _).symm @[simp] -lemma restrictFunctorComp_hom_app_app [IsOpenImmersion g] (M : Z.Modules) : +lemma restrictFunctorComp_hom_app_app (M : Z.Modules) : ((restrictFunctorComp f g).hom.app M).app U = M.presheaf.map (eqToHom (by simp)).op := rfl @[simp] -lemma restrictFunctorComp_inv_app_app [IsOpenImmersion g] (M : Z.Modules) : +lemma restrictFunctorComp_inv_app_app (M : Z.Modules) : ((restrictFunctorComp f g).inv.app M).app U = M.presheaf.map (eqToHom (by simp)).op := rfl /-- Restriction along equal morphisms are isomorphic. -/ @@ -424,7 +422,7 @@ lemma restrictFunctorCongr_inv_app_app {f g : X ⟶ Y} (hf : f = g) [IsOpenImmer ((restrictFunctorCongr hf).inv.app M).app U = M.presheaf.map (eqToHom (by simp [hf])).op := rfl /-- Restriction along open immersions commutes with taking stalks. -/ -def restrictStalkNatIso (f : X ⟶ Y) [IsOpenImmersion f] (x : X) : +def restrictStalkNatIso (x : X) : restrictFunctor f ⋙ toPresheaf _ ⋙ TopCat.Presheaf.stalkFunctor _ x ≅ toPresheaf _ ⋙ TopCat.Presheaf.stalkFunctor _ (f x) := haveI := Functor.initial_of_adjunction (f.isOpenEmbedding.adjunctionNhds x) @@ -433,8 +431,7 @@ def restrictStalkNatIso (f : X ⟶ Y) [IsOpenImmersion f] (x : X) : (Functor.Final.colimIso (f.isOpenEmbedding.functorNhds x).op) @[simp] -lemma germ_restrictStalkNatIso_hom_app (f : X ⟶ Y) [IsOpenImmersion f] - (x : X) (M : Y.Modules) (hxU : x ∈ U) : +lemma germ_restrictStalkNatIso_hom_app (x : X) (M : Y.Modules) (hxU : x ∈ U) : ((restrictFunctor f).obj M).presheaf.germ U _ hxU ≫ (restrictStalkNatIso f x).hom.app M = M.presheaf.germ _ _ (by simpa) := haveI := Functor.initial_of_adjunction (f.isOpenEmbedding.adjunctionNhds x) @@ -444,8 +441,7 @@ lemma germ_restrictStalkNatIso_hom_app (f : X ⟶ Y) [IsOpenImmersion f] set_option backward.isDefEq.respectTransparency false in @[simp] -lemma germ_restrictStalkNatIso_inv_app (f : X ⟶ Y) [IsOpenImmersion f] - (x : X) (M : Y.Modules) (hxU : x ∈ U) : +lemma germ_restrictStalkNatIso_inv_app (x : X) (M : Y.Modules) (hxU : x ∈ U) : M.presheaf.germ _ _ (by simpa) ≫ (restrictStalkNatIso f x).inv.app M = ((restrictFunctor f).obj M).presheaf.germ U _ hxU := by rw [← germ_restrictStalkNatIso_hom_app f x M hxU, Category.assoc, ← NatTrans.comp_app, diff --git a/Mathlib/AlgebraicTopology/ModelCategory/IsCofibrant.lean b/Mathlib/AlgebraicTopology/ModelCategory/IsCofibrant.lean index c10fcf13bab120..8676262d9c857f 100644 --- a/Mathlib/AlgebraicTopology/ModelCategory/IsCofibrant.lean +++ b/Mathlib/AlgebraicTopology/ModelCategory/IsCofibrant.lean @@ -52,8 +52,7 @@ lemma isCofibrant_of_cofibration [(cofibrations C).IsStableUnderComposition] section -variable (X Y : C) [(cofibrations C).IsStableUnderCobaseChange] [HasInitial C] - [HasBinaryCoproduct X Y] +variable (X Y : C) [(cofibrations C).IsStableUnderCobaseChange] [HasBinaryCoproduct X Y] instance [hY : IsCofibrant Y] : Cofibration (coprod.inl : X ⟶ X ⨿ Y) := by @@ -63,8 +62,7 @@ instance [hY : IsCofibrant Y] : ((IsPushout.of_isColimit_binaryCofan_of_isInitial (colimit.isColimit (pair X Y)) initialIsInitial).flip) hY -instance [HasInitial C] [HasBinaryCoproduct X Y] [hX : IsCofibrant X] : - Cofibration (coprod.inr : Y ⟶ X ⨿ Y) := by +instance [hX : IsCofibrant X] : Cofibration (coprod.inr : Y ⟶ X ⨿ Y) := by rw [isCofibrant_iff] at hX rw [cofibration_iff] at hX ⊢ exact MorphismProperty.of_isPushout @@ -102,7 +100,7 @@ lemma isFibrant_of_fibration [(fibrations C).IsStableUnderComposition] section -variable (X Y : C) [(fibrations C).IsStableUnderBaseChange] [HasTerminal C] +variable (X Y : C) [(fibrations C).IsStableUnderBaseChange] [HasBinaryProduct X Y] instance [hY : IsFibrant Y] : @@ -113,8 +111,7 @@ instance [hY : IsFibrant Y] : (IsPullback.of_isLimit_binaryFan_of_isTerminal (limit.isLimit (pair X Y)) terminalIsTerminal).flip hY -instance [HasTerminal C] [HasBinaryProduct X Y] [hX : IsFibrant X] : - Fibration (prod.snd : X ⨯ Y ⟶ Y) := by +instance [hX : IsFibrant X] : Fibration (prod.snd : X ⨯ Y ⟶ Y) := by rw [isFibrant_iff] at hX rw [fibration_iff] at hX ⊢ exact MorphismProperty.of_isPullback diff --git a/Mathlib/CategoryTheory/Adhesive/Subobject.lean b/Mathlib/CategoryTheory/Adhesive/Subobject.lean index 56e3ee54cfe521..f0ec0576e4d5ab 100644 --- a/Mathlib/CategoryTheory/Adhesive/Subobject.lean +++ b/Mathlib/CategoryTheory/Adhesive/Subobject.lean @@ -41,7 +41,7 @@ noncomputable def isColimitBinaryCofan (a b : Subobject X) : (by ext; simp [pullback.condition])) (by cat_disch)).hom) (by intros; rfl) (by intros; rfl) (by intros; rfl) -instance [Adhesive C] {X : C} : HasBinaryCoproducts (Subobject X) where +instance : HasBinaryCoproducts (Subobject X) where has_colimit F := by have : HasColimit (pair (F.obj ⟨WalkingPair.left⟩) (F.obj ⟨WalkingPair.right⟩)) := ⟨⟨⟨_, isColimitBinaryCofan (F.obj ⟨WalkingPair.left⟩) (F.obj ⟨WalkingPair.right⟩)⟩⟩⟩ diff --git a/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean b/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean index cd40542e01659d..1709e52a1dd283 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean @@ -805,7 +805,7 @@ open Limits variable {C : Type*} [Category* C] [HasZeroMorphisms C] (P : ObjectProperty C) -instance [HasZeroMorphisms C] : HasZeroMorphisms P.FullSubcategory where +instance : HasZeroMorphisms P.FullSubcategory where -- Note: Add zero field explicitly for a better transparency of definitional properties zero _ _ := { zero := P.homMk 0 } __ := P.fullyFaithfulι.hasZeroMorphisms diff --git a/Mathlib/CategoryTheory/Localization/Predicate.lean b/Mathlib/CategoryTheory/Localization/Predicate.lean index c0a5e2f8ffc2a1..ca88ecb7e2851b 100644 --- a/Mathlib/CategoryTheory/Localization/Predicate.lean +++ b/Mathlib/CategoryTheory/Localization/Predicate.lean @@ -232,6 +232,7 @@ the composition with a localization functor `L : C ⥤ D` with respect to def functorEquivalence : D ⥤ E ≌ W.FunctorsInverting E := (whiskeringLeftFunctor L W E).asEquivalence +set_option linter.overlappingInstances false in /-- The functor `(D ⥤ E) ⥤ (C ⥤ E)` given by the composition with a localization functor `L : C ⥤ D` with respect to `W : MorphismProperty C`. -/ @[nolint unusedArguments] diff --git a/Mathlib/CategoryTheory/Monoidal/Closed/Cartesian.lean b/Mathlib/CategoryTheory/Monoidal/Closed/Cartesian.lean index 5fdd055d657958..02f96505e73e9e 100644 --- a/Mathlib/CategoryTheory/Monoidal/Closed/Cartesian.lean +++ b/Mathlib/CategoryTheory/Monoidal/Closed/Cartesian.lean @@ -98,6 +98,7 @@ def powZero [BraidedCategory C] {I : C} (t : IsInitial I) [MonoidalClosed C] : I rw [← curry_natural_left, curry_eq_iff, ← cancel_epi (mulZero t).inv] apply t.hom_ext +set_option linter.overlappingInstances false in set_option backward.isDefEq.respectTransparency false in -- TODO: Generalise the below to its commuted variants. -- TODO: Define a distributive category, so that zero_mul and friends can be derived from this. diff --git a/Mathlib/CategoryTheory/Monoidal/Functor.lean b/Mathlib/CategoryTheory/Monoidal/Functor.lean index e58c23ef80b2d2..95735eae59447e 100644 --- a/Mathlib/CategoryTheory/Monoidal/Functor.lean +++ b/Mathlib/CategoryTheory/Monoidal/Functor.lean @@ -1220,7 +1220,7 @@ instance isMonoidal_refl : (Equivalence.refl (C := C)).IsMonoidal := set_option backward.isDefEq.respectTransparency false in /-- The inverse of a monoidal category equivalence is also a monoidal category equivalence. -/ -instance isMonoidal_symm [e.IsMonoidal] : e.symm.IsMonoidal where +instance isMonoidal_symm : e.symm.IsMonoidal where leftAdjoint_ε := by simp only [toAdjunction] dsimp [symm] diff --git a/Mathlib/CategoryTheory/Monoidal/Grp_.lean b/Mathlib/CategoryTheory/Monoidal/Grp_.lean index d679707572f455..9ac5eca435650a 100644 --- a/Mathlib/CategoryTheory/Monoidal/Grp_.lean +++ b/Mathlib/CategoryTheory/Monoidal/Grp_.lean @@ -351,8 +351,7 @@ lemma ext {X : C} (h₁ h₂ : GrpObj X) (H : h₁.toMonObj = h₂.toMonObj) : h set_option backward.isDefEq.respectTransparency false in /-- A monoid object with invertible homs is a group object. -/ @[implicit_reducible] -def ofInvertible (G : C) [CartesianMonoidalCategory C] [MonObj G] - (h : ∀ X (f : X ⟶ G), Invertible f) : GrpObj G where +def ofInvertible (G : C) [MonObj G] (h : ∀ X (f : X ⟶ G), Invertible f) : GrpObj G where inv := Yoneda.fullyFaithful.preimage ⟨fun X ↦ TypeCat.ofHom (fun f ↦ (h X.unop f).invOf), fun X Y f ↦ by ext g diff --git a/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean b/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean index d341fc53dc3356..6e54725f2f75aa 100644 --- a/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean +++ b/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean @@ -151,7 +151,7 @@ omit [Preadditive C] [Preadditive D] in instance : (Functor.whiskeringRight C D E).Additive where omit [Preadditive C] [Preadditive D] in -instance (F : C ⥤ D) [Preadditive E] : ((Functor.whiskeringLeft C D E).obj F).Additive where +instance (F : C ⥤ D) : ((Functor.whiskeringLeft C D E).obj F).Additive where omit [Preadditive D] in instance {E' : Type*} [Category* E'] [Preadditive E'] (G : C ⥤ D ⥤ E) (F : E ⥤ E') @@ -161,7 +161,7 @@ instance {E' : Type*} [Category* E'] [Preadditive E'] (G : C ⥤ D ⥤ E) (F : E set_option backward.isDefEq.respectTransparency false in universe w in -instance [HasCoproducts.{w} C] [Preadditive C] : (sigmaConst.{w} (C := C)).Additive where +instance [HasCoproducts.{w} C] : (sigmaConst.{w} (C := C)).Additive where end diff --git a/Mathlib/CategoryTheory/Shift/Adjunction.lean b/Mathlib/CategoryTheory/Shift/Adjunction.lean index 82a687d760a59e..871f7c43efbc7f 100644 --- a/Mathlib/CategoryTheory/Shift/Adjunction.lean +++ b/Mathlib/CategoryTheory/Shift/Adjunction.lean @@ -248,8 +248,6 @@ lemma mk' (_ : NatTrans.CommShift adj.unit A) : refine (compatibilityCounit_of_compatibilityUnit adj _ _ (fun X ↦ ?_) _).symm simpa [Functor.commShiftIso_comp_hom_app] using NatTrans.shift_app_comm adj.unit a X⟩ -variable [adj.CommShift A] - /-- The identity adjunction is compatible with the trivial `CommShift` structure on the identity functor. -/ diff --git a/Mathlib/CategoryTheory/Shift/Pullback.lean b/Mathlib/CategoryTheory/Shift/Pullback.lean index ab3ff639113ac1..411f8f881563dd 100644 --- a/Mathlib/CategoryTheory/Shift/Pullback.lean +++ b/Mathlib/CategoryTheory/Shift/Pullback.lean @@ -39,16 +39,17 @@ namespace CategoryTheory open Limits Category variable (C : Type*) [Category* C] {A B : Type*} [AddMonoid A] [AddMonoid B] - (φ : A →+ B) [HasShift C B] /-- The category `PullbackShift C φ` is equipped with a shift such that for all `a`, the shift functor by `a` is `shiftFunctor C (φ a)`. -/ @[nolint unusedArguments] -def PullbackShift (_ : A →+ B) [HasShift C B] := C +def PullbackShift [HasShift C B] (_ : A →+ B) := C deriving Category attribute [local instance] endofunctorMonoidalCategory +variable [HasShift C B] (φ : A →+ B) + set_option backward.isDefEq.respectTransparency false in /-- The shift on `PullbackShift C φ` is obtained by precomposing the shift on `C` with the monoidal functor `Discrete.addMonoidalFunctor φ : Discrete A ⥤ Discrete B`. -/ diff --git a/Mathlib/CategoryTheory/Sites/CoverLifting.lean b/Mathlib/CategoryTheory/Sites/CoverLifting.lean index e66aec7c3dc4a8..3bef9f03f243fe 100644 --- a/Mathlib/CategoryTheory/Sites/CoverLifting.lean +++ b/Mathlib/CategoryTheory/Sites/CoverLifting.lean @@ -350,7 +350,7 @@ alias sheafAdjunctionCocontinuous_homEquiv_apply_val := variable [HasWeakSheafify J A] [HasWeakSheafify K A] /-- The natural isomorphism exhibiting compatibility between pushforward and sheafification. -/ -def pushforwardContinuousSheafificationCompatibility [G.IsContinuous J K] : +def pushforwardContinuousSheafificationCompatibility : (whiskeringLeft _ _ A).obj G.op ⋙ presheafToSheaf J A ≅ presheafToSheaf K A ⋙ G.sheafPushforwardContinuous A J K := ((G.op.ranAdjunction A).comp (sheafificationAdjunction J A)).leftAdjointUniq diff --git a/Mathlib/CategoryTheory/Sites/DenseSubsite/Basic.lean b/Mathlib/CategoryTheory/Sites/DenseSubsite/Basic.lean index accdbaf6340868..1475d239a60767 100644 --- a/Mathlib/CategoryTheory/Sites/DenseSubsite/Basic.lean +++ b/Mathlib/CategoryTheory/Sites/DenseSubsite/Basic.lean @@ -674,8 +674,7 @@ variable (A) in and `sheafPushforwardContinuous G A J₀ J` is an equivalence of categories this is a sheafification functor `(Dᵒᵖ ⥤ A) ⥤ Sheaf K A` when `HasWeakSheafify J A` holds. -/ -noncomputable def sheafifyOfIsEquivalence - [IsEquivalence (sheafPushforwardContinuous G A J K)] : +noncomputable def sheafifyOfIsEquivalence : (Dᵒᵖ ⥤ A) ⥤ Sheaf K A := (whiskeringLeft _ _ _).obj G.op ⋙ presheafToSheaf J A ⋙ inv (G.sheafPushforwardContinuous A J K) @@ -685,8 +684,7 @@ variable (A) in and `sheafPushforwardContinuous G A J₀ J` is an equivalence of categories, this is the isomorphism between `sheafifyOfIsEquivalence J K G A ⋙ G.sheafPushforwardContinuous A J K` and the functor which sends a presheaf to the sheafification of its precomposition by `G.op`. -/ -noncomputable def sheafifyOfIsEquivalenceCompIso - [IsEquivalence (sheafPushforwardContinuous G A J K)] : +noncomputable def sheafifyOfIsEquivalenceCompIso : sheafifyOfIsEquivalence J K G A ⋙ G.sheafPushforwardContinuous A J K ≅ (whiskeringLeft _ _ _).obj G.op ⋙ presheafToSheaf J A := associator _ _ _ ≪≫ isoWhiskerLeft _ (associator _ _ _) ≪≫ diff --git a/Mathlib/CategoryTheory/Sites/Hypercover/One.lean b/Mathlib/CategoryTheory/Sites/Hypercover/One.lean index b8c287e46da5ef..cf59dca760ca51 100644 --- a/Mathlib/CategoryTheory/Sites/Hypercover/One.lean +++ b/Mathlib/CategoryTheory/Sites/Hypercover/One.lean @@ -76,8 +76,7 @@ section variable {i₁ i₂ : E.I₀} [HasPullback (E.f i₁) (E.f i₂)] /-- The obvious morphism `E.Y j ⟶ pullback (E.f i₁) (E.f i₂)` given by `E : PreOneHypercover S`. -/ -noncomputable abbrev toPullback (j : E.I₁ i₁ i₂) [HasPullback (E.f i₁) (E.f i₂)] : - E.Y j ⟶ pullback (E.f i₁) (E.f i₂) := +noncomputable abbrev toPullback (j : E.I₁ i₁ i₂) : E.Y j ⟶ pullback (E.f i₁) (E.f i₂) := pullback.lift (E.p₁ j) (E.p₂ j) (E.w j) variable (i₁ i₂) in diff --git a/Mathlib/CategoryTheory/Sites/Point/OfIsCofiltered.lean b/Mathlib/CategoryTheory/Sites/Point/OfIsCofiltered.lean index 71a371ce2eb924..c73aefee9ebafb 100644 --- a/Mathlib/CategoryTheory/Sites/Point/OfIsCofiltered.lean +++ b/Mathlib/CategoryTheory/Sites/Point/OfIsCofiltered.lean @@ -115,7 +115,7 @@ instance [IsCofiltered N] : (functor.{w} p).Initial := by (show fiberMk.{w} φ₁ = fiberMk.{w} φ₂ by simpa using hφ₁.trans hφ₂.symm) exact ⟨_, g, by cat_disch⟩ -instance [IsCofiltered N] [InitiallySmall.{w} N] : +instance [IsCofiltered N] : InitiallySmall.{w} (fiber.{w} p).Elements := initiallySmall_of_initial_of_initiallySmall (functor.{w} p) diff --git a/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean b/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean index c2549d0717fd98..ddbe0afeb4b496 100644 --- a/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean +++ b/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean @@ -116,7 +116,7 @@ variable [HasColimitsOfSize.{w, w} A] /-- Given a point of a site, the skyscraper presheaf functor is right adjoint to the fiber functor on presheaves. -/ -noncomputable def skyscraperPresheafAdjunction [HasColimitsOfSize.{w, w} A] : +noncomputable def skyscraperPresheafAdjunction : Φ.presheafFiber (A := A) ⊣ Φ.skyscraperPresheafFunctor := Adjunction.mkOfHomEquiv { homEquiv _ _ := Φ.skyscraperPresheafHomEquiv diff --git a/Mathlib/CategoryTheory/Sites/Pullback.lean b/Mathlib/CategoryTheory/Sites/Pullback.lean index 77b277c6460702..49940fe9efa501 100644 --- a/Mathlib/CategoryTheory/Sites/Pullback.lean +++ b/Mathlib/CategoryTheory/Sites/Pullback.lean @@ -72,7 +72,7 @@ def sheafPullback [HasWeakSheafify K A] : Sheaf J A ⥤ Sheaf K A := /-- The constructed `sheafPullback G A J K` is left adjoint to `G.sheafPushforwardContinuous A J K`. -/ -def sheafAdjunctionContinuous [Functor.IsContinuous G J K] [HasWeakSheafify K A] : +def sheafAdjunctionContinuous [HasWeakSheafify K A] : sheafPullback G A J K ⊣ G.sheafPushforwardContinuous A J K := ((G.op.lanAdjunction A).comp (sheafificationAdjunction K A)).restrictFullyFaithful (fullyFaithfulSheafToPresheaf J A) (Functor.FullyFaithful.id _) (Iso.refl _) (Iso.refl _) diff --git a/Mathlib/CategoryTheory/Triangulated/Functor.lean b/Mathlib/CategoryTheory/Triangulated/Functor.lean index 151e8763d9e80b..a73c2c0e1fe6c4 100644 --- a/Mathlib/CategoryTheory/Triangulated/Functor.lean +++ b/Mathlib/CategoryTheory/Triangulated/Functor.lean @@ -129,7 +129,7 @@ def mapTriangleRotateIso : set_option backward.isDefEq.respectTransparency false in /-- `F.mapTriangle` commutes with the inverse of the rotation of triangles. -/ @[simps!] -noncomputable def mapTriangleInvRotateIso [F.Additive] : +noncomputable def mapTriangleInvRotateIso : F.mapTriangle ⋙ Pretriangulated.invRotate D ≅ Pretriangulated.invRotate C ⋙ F.mapTriangle := NatIso.ofComponents diff --git a/Mathlib/Control/Applicative.lean b/Mathlib/Control/Applicative.lean index 79ba19c39092dc..b317eef1c573ae 100644 --- a/Mathlib/Control/Applicative.lean +++ b/Mathlib/Control/Applicative.lean @@ -40,6 +40,7 @@ theorem Applicative.map_seq_map (f : α → β → γ) (g : σ → β) (x : F α theorem Applicative.pure_seq_eq_map' (f : α → β) : ((pure f : F (α → β)) <*> ·) = (f <$> ·) := by simp [functor_norm] +set_option linter.overlappingInstances false in theorem Applicative.ext {F} : ∀ {A1 : Applicative F} {A2 : Applicative F} [@LawfulApplicative F A1] [@LawfulApplicative F A2], (∀ {α : Type u} (x : α), @Pure.pure _ A1.toPure _ x = @Pure.pure _ A2.toPure _ x) → diff --git a/Mathlib/Control/Functor.lean b/Mathlib/Control/Functor.lean index 37046a426e48de..f428d9e905aa0c 100644 --- a/Mathlib/Control/Functor.lean +++ b/Mathlib/Control/Functor.lean @@ -43,6 +43,7 @@ theorem Functor.map_comp_map (f : α → β) (g : β → γ) : ((g <$> ·) ∘ (f <$> ·) : F α → F γ) = ((g ∘ f) <$> ·) := funext fun _ => (comp_map _ _ _).symm +set_option linter.overlappingInstances false in theorem Functor.ext {F} : ∀ {F1 : Functor F} {F2 : Functor F} [@LawfulFunctor F F1] [@LawfulFunctor F F2], (∀ (α β) (f : α → β) (x : F α), @Functor.map _ F1 _ _ f x = @Functor.map _ F2 _ _ f x) → diff --git a/Mathlib/GroupTheory/GroupAction/Hom.lean b/Mathlib/GroupTheory/GroupAction/Hom.lean index f569f8bad2e5f9..b49d342ac17c18 100644 --- a/Mathlib/GroupTheory/GroupAction/Hom.lean +++ b/Mathlib/GroupTheory/GroupAction/Hom.lean @@ -607,15 +607,6 @@ class DistribMulActionSemiHomClass (F : Type*) [FunLike F A B] : Prop extends MulActionSemiHomClass F φ A B, AddMonoidHomClass F A B -/-- `DistribMulActionHomClass F M A B` states that `F` is a type of morphisms preserving - the additive monoid structure and equivariant with respect to the action of `M`. - It is an abbreviation to `DistribMulActionHomClass F (MonoidHom.id M) A B` -You should extend this class when you extend `DistribMulActionHom`. -/ -abbrev DistribMulActionHomClass (F : Type*) (M : outParam Type*) - (A B : outParam Type*) [Monoid M] [AddMonoid A] [AddMonoid B] - [DistribMulAction M A] [DistribMulAction M B] [FunLike F A B] := - DistribMulActionSemiHomClass F (MonoidHom.id M) A B - /-- `MulDistribMulActionSemiHomClass F φ A B` states that `F` is a type of morphisms preserving the monoid structure and equivariant with respect to `φ`. You should extend this class when you extend `MulDistribMulActionSemiHom`. -/ @@ -632,22 +623,19 @@ class MulDistribMulActionSemiHomClass (F : Type*) the monoid structure and equivariant with respect to the action of `M`. It is an abbreviation to `MulDistribMulActionHomClass F (MonoidHom.id M) A B` You should extend this class when you extend `MulDistribMulActionHom`. -/ -@[to_additive existing (dont_translate := M) DistribMulActionHomClass] +@[to_additive (dont_translate := M) DistribMulActionHomClass +/-- `DistribMulActionHomClass F M A B` states that `F` is a type of morphisms preserving + the additive monoid structure and equivariant with respect to the action of `M`. + It is an abbreviation to `DistribMulActionHomClass F (MonoidHom.id M) A B` +You should extend this class when you extend `DistribMulActionHom`. -/] abbrev MulDistribMulActionHomClass (F : Type*) (M : outParam Type*) (A B : outParam Type*) [Monoid M] [Monoid A] [Monoid B] [MulDistribMulAction M A] [MulDistribMulAction M B] [FunLike F A B] := MulDistribMulActionSemiHomClass F (MonoidHom.id M) A B -instance DistribMulAction.instFunLike [AddMonoid A] [DistribMulAction M A] [AddMonoid B] - [DistribMulAction N B] : FunLike (A →ₑ+[φ] B) A B where - coe m := m.toFun - coe_injective' f g h := by - rcases f with ⟨tF, _, _⟩; rcases g with ⟨tG, _, _⟩ - cases tF; cases tG; congr - namespace MulDistribMulActionHom -@[to_additive existing (dont_translate := M N) DistribMulAction.instFunLike] +@[to_additive (dont_translate := M N)] instance : FunLike (A →ₑ*[φ] B) A B where coe m := m.toFun coe_injective' f g h := by @@ -663,20 +651,13 @@ instance : MulDistribMulActionSemiHomClass (A →ₑ*[φ] B) φ A B where variable {φ φ' A B B₁} variable {F : Type*} [FunLike F A B] -/-- Turn an element of a type `F` satisfying `DistribMulActionHomClass F M X Y` into an actual -`DistribMulActionHom`. This is declared as the default coercion from `F` to -`DistribMulActionHom M X Y`. -/ -def _root_.DistribMulActionSemiHomClass.toDistribMulActionHom - [AddMonoid A] [DistribMulAction M A] [AddMonoid B] [DistribMulAction N B] - [DistribMulActionSemiHomClass F φ A B] - (f : F) : A →ₑ+[φ] B := - { (f : A →+ B), (f : A →ₑ[φ] B) with } - /-- Turn an element of a type `F` satisfying `MulDistribMulActionHomClass F M X Y` into an actual `MulDistribMulActionHom`. This is declared as the default coercion from `F` to `MulDistribMulActionHom M X Y`. -/ -@[to_additive existing (attr := coe) (dont_translate := M N) - DistribMulActionSemiHomClass.toDistribMulActionHom] +@[to_additive (attr := coe) (dont_translate := M N) toDistribMulActionHom +/-- Turn an element of a type `F` satisfying `DistribMulActionHomClass F M X Y` into an actual +`DistribMulActionHom`. This is declared as the default coercion from `F` to +`DistribMulActionHom M X Y`. -/] def _root_.MulDistribMulActionSemiHomClass.toMulDistribMulActionHom [MulDistribMulActionSemiHomClass F φ A B] (f : F) : A →ₑ*[φ] B := @@ -751,12 +732,9 @@ protected theorem map_smulₑ (f : A →ₑ*[φ] B) (m : M) (x : A) : f (m • x variable (M) -/-- The identity map as an equivariant additive monoid homomorphism. -/ -protected def _root_.DistribMulActionHom.id [AddMonoid A] [DistribMulAction M A] : A →+[M] A := - ⟨MulActionHom.id _, rfl, fun _ _ => rfl⟩ - /-- The identity map as an equivariant monoid homomorphism. -/ -@[to_additive existing (dont_translate := M)] +@[to_additive (dont_translate := M) +/-- The identity map as an equivariant additive monoid homomorphism. -/] protected def id : A →*[M] A := ⟨MulActionHom.id _, rfl, fun _ _ => rfl⟩ @@ -767,11 +745,12 @@ theorem id_apply (x : A) : MulDistribMulActionHom.id M x = x := by variable {M C ψ χ} instance _root_.DistriMulActionHom.instZero - [AddMonoid A] [DistribMulAction M A] [AddMonoid B] [DistribMulAction N B] : Zero (A →ₑ+[φ] B) := + {A : Type*} [AddMonoid A] [DistribMulAction M A] + {B : Type*} [AddMonoid B] [DistribMulAction N B] : Zero (A →ₑ+[φ] B) := ⟨{ (0 : A →+ B) with map_smul' := fun m _ => by simp }⟩ @[to_additive (dont_translate := M)] -instance [Monoid A] [MulDistribMulAction M A] : One (A →*[M] A) := +instance : One (A →*[M] A) := ⟨MulDistribMulActionHom.id M⟩ @[simp] @@ -791,22 +770,16 @@ theorem _root_.DistriMulActionHom.zero_apply {A : Type*} [AddMonoid A] [DistribM theorem one_apply (a : A) : (1 : A →*[M] A) a = a := rfl -instance [AddMonoid A] [DistribMulAction M A] [AddMonoid B] [DistribMulAction N B] : +instance {A : Type*} [AddMonoid A] [DistribMulAction M A] + {B : Type*} [AddMonoid B] [DistribMulAction N B] : Inhabited (A →ₑ+[φ] B) := ⟨0⟩ -/-- Composition of two equivariant additive monoid homomorphisms. -/ -def _root_.DistribMulActionHom.comp [AddMonoid A] [DistribMulAction M A] - [AddMonoid B] [DistribMulAction N B] [AddMonoid C] [DistribMulAction P C] - (g : B →ₑ+[ψ] C) (f : A →ₑ+[φ] B) - [κ : MonoidHom.CompTriple φ ψ χ] : A →ₑ+[χ] C := - { MulActionHom.comp (g : B →ₑ[ψ] C) (f : A →ₑ[φ] B), - AddMonoidHom.comp (g : B →+ C) (f : A →+ B) with } - /-- Composition of two equivariant monoid homomorphisms. -/ -@[to_additive existing (dont_translate := M N P)] -def comp (g : B →ₑ*[ψ] C) (f : A →ₑ*[φ] B) - [κ : MonoidHom.CompTriple φ ψ χ] : A →ₑ*[χ] C := +@[to_additive (dont_translate := M N P) +/-- Composition of two equivariant additive monoid homomorphisms. -/] +def comp [κ : MonoidHom.CompTriple φ ψ χ] + (g : B →ₑ*[ψ] C) (f : A →ₑ*[φ] B) : A →ₑ*[χ] C := { MulActionHom.comp (g : B →ₑ[ψ] C) (f : A →ₑ[φ] B), MonoidHom.comp (g : B →* C) (f : A →* B) with } @@ -831,14 +804,9 @@ theorem comp_assoc {Q D : Type*} [Monoid Q] [Monoid D] [MulDistribMulAction Q D] h.comp (g.comp f) = (h.comp g).comp f := ext fun _ => rfl -/-- The inverse of a bijective `DistribMulActionHom` is a `DistribMulActionHom`. -/ -def _root_.DistribMulActionHom.inverse [AddMonoid A] [DistribMulAction M A] - [AddMonoid B₁] [DistribMulAction M B₁] (f : A →+[M] B₁) (g : B₁ → A) - (h₁ : Function.LeftInverse g f) (h₂ : Function.RightInverse g f) : B₁ →+[M] A := - { (f : A →+ B₁).inverse g h₁ h₂, f.toMulActionHom.inverse g h₁ h₂ with toFun := g } - /-- The inverse of a bijective `MulDistribMulActionHom` is a `MulDistribMulActionHom`. -/ -@[to_additive existing (attr := simp) (dont_translate := M)] +@[to_additive (attr := simp) (dont_translate := M) +/-- The inverse of a bijective `DistribMulActionHom` is a `DistribMulActionHom`. -/] def inverse (f : A →*[M] B₁) (g : B₁ → A) (h₁ : Function.LeftInverse g f) (h₂ : Function.RightInverse g f) : B₁ →*[M] A := { (f : A →* B₁).inverse g h₁ h₂, f.toMulActionHom.inverse g h₁ h₂ with toFun := g } diff --git a/Mathlib/GroupTheory/OreLocalization/Basic.lean b/Mathlib/GroupTheory/OreLocalization/Basic.lean index f6867fd3b5a6db..3510c03ad063e9 100644 --- a/Mathlib/GroupTheory/OreLocalization/Basic.lean +++ b/Mathlib/GroupTheory/OreLocalization/Basic.lean @@ -548,6 +548,7 @@ protected def hsmul (c : R) : rw [← mul_one (oreDenom (c • 1) s), ← oreDiv_smul_oreDiv, ← mul_one (oreDenom (c • 1) _), ← oreDiv_smul_oreDiv, ← OreLocalization.expand]) +set_option linter.overlappingInstances false in /- Warning: This gives a diamond on `SMul R[S⁻¹] M[S⁻¹][S⁻¹]`, but we will almost never localize at the same monoid twice. -/ /- Although the definition does not require `IsScalarTower R M X`, diff --git a/Mathlib/LinearAlgebra/FreeModule/Basic.lean b/Mathlib/LinearAlgebra/FreeModule/Basic.lean index 89a3e24dc7b431..ef757642801014 100644 --- a/Mathlib/LinearAlgebra/FreeModule/Basic.lean +++ b/Mathlib/LinearAlgebra/FreeModule/Basic.lean @@ -110,7 +110,7 @@ instance [Nontrivial M] : Nonempty (Module.Free.ChooseBasisIndex R M) := theorem infinite [Infinite R] [Nontrivial M] : Infinite M := (Equiv.infinite_iff (chooseBasis R M).repr.toEquiv).mpr Finsupp.infinite_of_right -instance [Module.Free R M] [Nontrivial M] : FaithfulSMul R M := +instance [Nontrivial M] : FaithfulSMul R M := .of_injective _ (chooseBasis R M).repr.symm.injective variable {R M N} @@ -146,7 +146,7 @@ lemma iff_of_equiv {R R' M M'} [Semiring R] [AddCommMonoid M] [Module R M] @[deprecated (since := "2026-02-14")] alias of_ringEquiv := of_equiv @[deprecated (since := "2026-02-14")] alias iff_of_ringEquiv := iff_of_equiv -instance Module.free_shrink [Module.Free R M] [Small.{w} M] : Module.Free R (Shrink.{w} M) := +instance Module.free_shrink [Small.{w} M] : Module.Free R (Shrink.{w} M) := Module.Free.of_equiv (Shrink.linearEquiv R M).symm variable (R M N) @@ -155,7 +155,7 @@ variable (R M N) instance self : Module.Free R R := of_basis (Basis.singleton Unit R) -instance ulift [Free R M] : Free R (ULift M) := of_equiv ULift.moduleEquiv.symm +instance ulift : Free R (ULift M) := of_equiv ULift.moduleEquiv.symm instance (priority := 100) of_subsingleton [Subsingleton N] : Module.Free R N := of_basis.{u, z, z} (Basis.empty N : Basis PEmpty R N) diff --git a/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean b/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean index 56ed89fc110a60..b836d59d6f3d8e 100644 --- a/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean +++ b/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean @@ -348,7 +348,7 @@ variable [Fact (Even (Fintype.card n))] /-- Formal operation of negation on special linear group on even cardinality `n` given by negating each element. -/ -instance instNeg [Fact (Even (Fintype.card n))] : Neg (SpecialLinearGroup n R) := +instance instNeg : Neg (SpecialLinearGroup n R) := ⟨fun g => ⟨-g, by simpa [(@Fact.out <| Even <| Fintype.card n).neg_one_pow, g.det_coe] using det_smul (↑ₘg) (-1)⟩⟩ diff --git a/Mathlib/Logic/Basic.lean b/Mathlib/Logic/Basic.lean index 03157c26576f2b..08dbcff5698c2f 100644 --- a/Mathlib/Logic/Basic.lean +++ b/Mathlib/Logic/Basic.lean @@ -1004,6 +1004,7 @@ theorem beq_ext {α : Type*} (inst1 : BEq α) (inst2 : BEq α) funext x y exact h x y +set_option linter.overlappingInstances false in theorem lawful_beq_subsingleton {α : Type*} (inst1 : BEq α) (inst2 : BEq α) [@LawfulBEq α inst1] [@LawfulBEq α inst2] : inst1 = inst2 := by diff --git a/Mathlib/Order/MinMax.lean b/Mathlib/Order/MinMax.lean index ff23594495ac47..7fac2063b6a5fc 100644 --- a/Mathlib/Order/MinMax.lean +++ b/Mathlib/Order/MinMax.lean @@ -39,7 +39,7 @@ theorem le_max_iff : a ≤ max b c ↔ a ≤ b ∨ a ≤ c := le_sup_iff @[to_dual] -instance [LinearOrder α] : Std.LawfulOrderSup α where +instance : Std.LawfulOrderSup α where max_le_iff _ _ _ := max_le_iff @[to_dual max_lt_iff] diff --git a/Mathlib/RepresentationTheory/Rep/Res.lean b/Mathlib/RepresentationTheory/Rep/Res.lean index 509a320cccc6b0..b87be09d3fdce1 100644 --- a/Mathlib/RepresentationTheory/Rep/Res.lean +++ b/Mathlib/RepresentationTheory/Rep/Res.lean @@ -91,8 +91,7 @@ abbrev ofQuotient : Rep k (G ⧸ S) := Rep.of (A.ρ.ofQuotient S) /-- A `G`-representation `A` on which a normal subgroup `S ≤ G` acts trivially induces a `G ⧸ S`-representation on `A`, and composing this with the quotient map `G → G ⧸ S` gives the original representation by definition. Useful for typechecking. -/ -abbrev resOfQuotientIso [Representation.IsTrivial (A.ρ.comp S.subtype)] : - (res (QuotientGroup.mk' S) (A.ofQuotient S)) ≅ A := Iso.refl _ +abbrev resOfQuotientIso : (res (QuotientGroup.mk' S) (A.ofQuotient S)) ≅ A := Iso.refl _ end diff --git a/Mathlib/RingTheory/AdicCompletion/AsTensorProduct.lean b/Mathlib/RingTheory/AdicCompletion/AsTensorProduct.lean index 467cd40cce7071..9ba431510b2ce0 100644 --- a/Mathlib/RingTheory/AdicCompletion/AsTensorProduct.lean +++ b/Mathlib/RingTheory/AdicCompletion/AsTensorProduct.lean @@ -343,7 +343,7 @@ lemma tensor_map_id_left_injective_of_injective (hf : Function.Injective f) : end /-- Adic completion of a Noetherian ring `R` is flat over `R`. -/ -instance flat_of_isNoetherian [IsNoetherianRing R] : Module.Flat R (AdicCompletion I R) := +instance flat_of_isNoetherian : Module.Flat R (AdicCompletion I R) := Module.Flat.iff_lTensor_injective'.mpr fun J ↦ tensor_map_id_left_injective_of_injective I (Submodule.injective_subtype J) diff --git a/Mathlib/RingTheory/Algebraic/Integral.lean b/Mathlib/RingTheory/Algebraic/Integral.lean index 013f62c2144576..894cfd69ab8dfa 100644 --- a/Mathlib/RingTheory/Algebraic/Integral.lean +++ b/Mathlib/RingTheory/Algebraic/Integral.lean @@ -552,10 +552,6 @@ variable (R S) [NoZeroDivisors R] theorem rank_polynomial_polynomial : Module.rank R[X] S[X] = Module.rank R S := ((Algebra.isPushout_iff ..).mp inferInstance).rank_eq -#adaptation_note /-- Needed after leanprover/lean4#12564 -/ -noncomputable instance (σ : Type u) [Algebra R S] : Module R (MvPolynomial σ S) := - inferInstanceAs <| Module R (AddMonoidAlgebra S (σ →₀ ℕ)) - theorem rank_mvPolynomial_mvPolynomial (σ : Type u) : Module.rank (MvPolynomial σ R) (MvPolynomial σ S) = Cardinal.lift.{u} (Module.rank R S) := by have := Algebra.isPushout_iff R (MvPolynomial σ R) S (MvPolynomial σ S) diff --git a/Mathlib/RingTheory/DedekindDomain/Ideal/Basic.lean b/Mathlib/RingTheory/DedekindDomain/Ideal/Basic.lean index 627d063a660d3a..2ba71c65711f2d 100644 --- a/Mathlib/RingTheory/DedekindDomain/Ideal/Basic.lean +++ b/Mathlib/RingTheory/DedekindDomain/Ideal/Basic.lean @@ -347,7 +347,7 @@ lemma mul_left_strictMono {I : FractionalIdeal A⁰ K} (hI : I ≠ 0) : StrictMo lemma mul_right_strictMono {I : FractionalIdeal A⁰ K} (hI : I ≠ 0) : StrictMono (I * ·) := fun _J _K hJK ↦ mul_lt_mul_of_pos_left hJK <| pos_iff_ne_zero.2 hI -instance [IsDedekindDomain A] : PosMulReflectLE (Ideal A) where +instance : PosMulReflectLE (Ideal A) where elim I J K e := by dsimp rwa [← FractionalIdeal.coeIdeal_le_coeIdeal (FractionRing A), diff --git a/Mathlib/RingTheory/GradedAlgebra/Basic.lean b/Mathlib/RingTheory/GradedAlgebra/Basic.lean index 18f52ca0e09c00..44a6e7d096a021 100644 --- a/Mathlib/RingTheory/GradedAlgebra/Basic.lean +++ b/Mathlib/RingTheory/GradedAlgebra/Basic.lean @@ -186,11 +186,11 @@ abbrev GradedAlgebra.ofAlgHom [SetLike.GradedMonoid 𝒜] (decompose : A →ₐ[ ext i x : 2 exact (decompose.congr_arg <| DirectSum.coeAlgHom_of _ _ _).trans (left_inv i x) -variable [GradedAlgebra 𝒜] - instance (R₀ : Type*) [CommSemiring R₀] [Algebra R₀ R] [Algebra R₀ A] [IsScalarTower R₀ R A] [i : GradedAlgebra 𝒜] : GradedAlgebra (𝒜 · |>.restrictScalars R₀) := { i with } +variable [GradedAlgebra 𝒜] + namespace DirectSum /-- If `A` is graded by `ι` with degree `i` component `𝒜 i`, then it is isomorphic as diff --git a/Mathlib/RingTheory/Localization/AtPrime/Basic.lean b/Mathlib/RingTheory/Localization/AtPrime/Basic.lean index ca5324dbe6b9cd..b3d84d4149714e 100644 --- a/Mathlib/RingTheory/Localization/AtPrime/Basic.lean +++ b/Mathlib/RingTheory/Localization/AtPrime/Basic.lean @@ -568,8 +568,7 @@ variable (S Sₚ) in The isomorphism `S ⧸ pS ≃+* Sₚ ⧸ p·Sₚ`, where `Sₚ` is the localization of `S` at the (image) of the complement of `p` -/ -noncomputable def equivQuotientMapMaximalIdeal [p.IsMaximal] : - S ⧸ pS ≃+* Sₚ ⧸ pSₚ := by +noncomputable def equivQuotientMapMaximalIdeal : S ⧸ pS ≃+* Sₚ ⧸ pSₚ := by haveI h : pSₚ = Ideal.map (algebraMap S Sₚ) pS := by rw [← map_eq_maximalIdeal p, Ideal.map_map, ← IsScalarTower.algebraMap_eq, Ideal.map_map, ← IsScalarTower.algebraMap_eq] diff --git a/Mathlib/RingTheory/Localization/AtPrime/Extension.lean b/Mathlib/RingTheory/Localization/AtPrime/Extension.lean index 430650d30ec73f..4c381b0ca0976f 100644 --- a/Mathlib/RingTheory/Localization/AtPrime/Extension.lean +++ b/Mathlib/RingTheory/Localization/AtPrime/Extension.lean @@ -103,7 +103,7 @@ the complement of `p` and `P` is a maximal ideal of `S` above `p`. Note that this isomorphism makes the obvious diagram involving `R ⧸ p ≃+* Rₚ ⧸ maximalIdeal Rₚ` commute, see `IsLocalization.AtPrime.algebraMap_equivQuotMaximalIdeal_symm_apply`. -/ -noncomputable def equivQuotientMapOfIsMaximal [p.IsPrime] [P.IsMaximal] : +noncomputable def equivQuotientMapOfIsMaximal [P.IsMaximal] : S ⧸ P ≃+* Sₚ ⧸ P.map (algebraMap S Sₚ) := .trans (Ideal.quotEquivOfEq (by diff --git a/Mathlib/RingTheory/Morita/Matrix.lean b/Mathlib/RingTheory/Morita/Matrix.lean index cbcea31c61af09..7ee34e99e333e3 100644 --- a/Mathlib/RingTheory/Morita/Matrix.lean +++ b/Mathlib/RingTheory/Morita/Matrix.lean @@ -76,8 +76,8 @@ variable {R} in from `M` to `N`. -/ @[simps!] def fromMatrixLinear {N : Type*} [AddCommGroup N] [Module (Matrix ι ι R) N] (i : ι) - [Module R N] [IsScalarTower R (Matrix ι ι R) N] [IsScalarTower R (Matrix ι ι R) M] - (f : M →ₗ[Matrix ι ι R] N) : toModuleCatObj R M i →ₗ[R] toModuleCatObj R N i := + [Module R N] [IsScalarTower R (Matrix ι ι R) N] (f : M →ₗ[Matrix ι ι R] N) : + toModuleCatObj R M i →ₗ[R] toModuleCatObj R N i := f.restrictScalars R |>.restrict fun x hx => by obtain ⟨y, rfl⟩ := mem_toModuleCatObj i |>.1 hx exact ⟨f y, map_smul _ _ _ |>.symm⟩ diff --git a/Mathlib/Tactic/Translate/Reorder.lean b/Mathlib/Tactic/Translate/Reorder.lean index e213913f2b0df8..adde75e42ca9d9 100644 --- a/Mathlib/Tactic/Translate/Reorder.lean +++ b/Mathlib/Tactic/Translate/Reorder.lean @@ -78,7 +78,7 @@ where /-- Permute the array using a sequence of indices defining a cyclic permutation. If the list of indices `l = [i₁, i₂, ..., iₙ]` are all distinct then `(cyclicPermute! a l)[iₖ₊₁] = a[iₖ]` and `(cyclicPermute! a l)[i₀] = a[iₙ]` -/ - cyclicPermute! [Inhabited α] : Array α → List Nat → Array α + cyclicPermute! : Array α → List Nat → Array α | a, [] => a | a, i :: is => cyclicPermuteAux a is a[i]! i cyclicPermuteAux : Array α → List Nat → α → Nat → Array α diff --git a/Mathlib/Topology/Algebra/MulAction.lean b/Mathlib/Topology/Algebra/MulAction.lean index 1833af008e4ac6..189be8eab304bd 100644 --- a/Mathlib/Topology/Algebra/MulAction.lean +++ b/Mathlib/Topology/Algebra/MulAction.lean @@ -330,6 +330,7 @@ theorem continuousSMul_iInf {ts' : ι → TopologicalSpace X} (h : ∀ i, @ContinuousSMul M X _ _ (ts' i)) : @ContinuousSMul M X _ _ (⨅ i, ts' i) := continuousSMul_sInf <| Set.forall_mem_range.mpr h +set_option linter.overlappingInstances false in @[to_additive] theorem continuousSMul_inf {t₁ t₂ : TopologicalSpace X} [@ContinuousSMul M X _ _ t₁] [@ContinuousSMul M X _ _ t₂] : @ContinuousSMul M X _ _ (t₁ ⊓ t₂) := by diff --git a/Mathlib/Topology/Compactness/CompactlyGeneratedSpace.lean b/Mathlib/Topology/Compactness/CompactlyGeneratedSpace.lean index 896c2d06bfa1f1..d53c24de2df703 100644 --- a/Mathlib/Topology/Compactness/CompactlyGeneratedSpace.lean +++ b/Mathlib/Topology/Compactness/CompactlyGeneratedSpace.lean @@ -364,7 +364,7 @@ instance to_compactlyCoherentSpace [CompactlyGeneratedSpace X] : CompactlyCohere fun K _ _ _ f hf ↦ h K f hf /-- A compactly coherent space that is Hausdorff is compactly generated. -/ -instance of_compactlyCoherentSpace_of_t2 [T2Space X] [CompactlyCoherentSpace X] : +instance of_compactlyCoherentSpace_of_t2 [CompactlyCoherentSpace X] : CompactlyGeneratedSpace X := by apply compactlyGeneratedSpace_of_isClosed_of_t2 intro s hs From 3dec9160e8454a7e8c7090290516945d2f5b0cbb Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 17 Apr 2026 03:43:36 +0200 Subject: [PATCH 089/141] fix --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 12 +++++------- MathlibTest/Subsingleton.lean | 1 + MathlibTest/Variable.lean | 2 ++ MathlibTest/WhitespaceLinter.lean | 1 + MathlibTest/congr.lean | 2 ++ 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 550ed312e1acfb..30a5204699122d 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -19,15 +19,11 @@ non-defeq versions of that data. This situation, both for declarations and more as an "instance diamond". This linter warns against declarations whose local contexts include multiple versions of the same data. -This is a hybrid syntax and environment linter.For performance reasons, the syntax linter **only** -runs interactively in the language server. It will not run on the command line during a typical -`lake build`. The syntax linter also only lints the bodies of declarations that appear in source, -and does not currently handle declarations that do not have a "body" such as `structure`s. (The -environment linter does handle these cases.) +This is a syntax linter. It is run on partially and fully elaborated declarations. Note that since all proofs of a given proposition are definitionally equal, multiple different ways -of obtaining instances of `Prop` classes pose no issue. Hence, this linter only warns against -data-carrying instance projections. +of obtaining instances of `Prop` classes pose no issue. So for `Prop` classes, this linter only +warns when the same class is assumed multiple times. Note that since this linter also warns against the trivial case of the same data-carrying instance appearing twice, it warns against explicit local instance hypotheses which shadow `variable`s. @@ -148,6 +144,8 @@ def overlapsToMsg (overlaps : Std.HashMap Expr (Array FVarId)) (ctx : ContextInf MetaM MessageData := do let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := overlaps.fold (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) + -- Sort the suggestions in a somewhat fvarId-independent way + let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] for (fvars, overlaps) in sortedOverlaps do let parents ← fvars.mapM (do instantiateMVars <| ← ·.getType) diff --git a/MathlibTest/Subsingleton.lean b/MathlibTest/Subsingleton.lean index 6b5fd394eacca6..f139fe25b410b0 100644 --- a/MathlibTest/Subsingleton.lean +++ b/MathlibTest/Subsingleton.lean @@ -56,6 +56,7 @@ end AvoidSurprise /-! Handles `BEq` instances if there are `LawfulBEq` instances for each. -/ +set_option linter.overlappingInstances false in example (α : Type) (inst1 inst2 : BEq α) [@LawfulBEq α inst1] [@LawfulBEq α inst2] : inst1 = inst2 := by subsingleton diff --git a/MathlibTest/Variable.lean b/MathlibTest/Variable.lean index 3c921952a845aa..de4174b1179fc9 100644 --- a/MathlibTest/Variable.lean +++ b/MathlibTest/Variable.lean @@ -194,6 +194,8 @@ info: Try this: -/ #guard_msgs in variable? [VectorSpace k V] [Algebra k V] + +set_option linter.overlappingInstances false example : Field k := inferInstance example : AddCommGroup V := inferInstance example : Module k V := inferInstance diff --git a/MathlibTest/WhitespaceLinter.lean b/MathlibTest/WhitespaceLinter.lean index d80cbd5576ee0a..664907eedb41e0 100644 --- a/MathlibTest/WhitespaceLinter.lean +++ b/MathlibTest/WhitespaceLinter.lean @@ -315,6 +315,7 @@ omit [h : Add Nat] [Add Nat] -- Include statements are not linted. include h +set_option linter.overlappingInstances false in /-- warning: extra space in the source diff --git a/MathlibTest/congr.lean b/MathlibTest/congr.lean index 6059cfc331d35a..37a8d68ede7332 100644 --- a/MathlibTest/congr.lean +++ b/MathlibTest/congr.lean @@ -326,10 +326,12 @@ example {α} [AddCommMonoid α] [PartialOrder α] {a b c d e f g : α} : Lawful BEq instances are "subsingletons". -/ +set_option linter.overlappingInstances false in example (inst1 : BEq α) [LawfulBEq α] (inst2 : BEq α) [LawfulBEq α] (xs : List α) (x : α) : @List.erase _ inst1 xs x = @List.erase _ inst2 xs x := by congr! +set_option linter.overlappingInstances false in /-- error: unsolved goals case h.e'_2 From cfd593f78d6ee396cacc06e8f88e5d6185ac2f06 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 17 Apr 2026 10:40:00 +0200 Subject: [PATCH 090/141] does specialization make a difference? --- Mathlib/Lean/Elab/InfoTree.lean | 18 +++++++++++++++++- .../Tactic/Linter/OverlappingInstances.lean | 3 +-- MathlibTest/OverlappingInstances.lean | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index 9755b678eea600..93a1928362f4a6 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -130,11 +130,27 @@ def getDeclsByBody (t : InfoTree) : List Name := else decls | _ => decls +/-- A copy of `foldInfoTree`, but with the `@[specialize]` attribute. -/ +@[specialize] +partial def foldInfoTree' {α} (init : α) (f : ContextInfo → InfoTree → α → α) : InfoTree → α := + go none init +where + /-- `foldInfoTree.go` is like `foldInfoTree` but with an additional outer context parameter `ctx?`. -/ + @[specialize] + go ctx? a + | context ctx t => go (ctx.mergeIntoOuter? ctx?) a t + | t@(node i ts) => + let a := match ctx? with + | none => a + | some ctx => f ctx t a + ts.foldl (init := a) (go <| i.updateContext? ctx?) + | hole _ => a + /-- Gets the first child info of each `Lean.Elab.BodyInfo`, which should be the only child, and should be a `TermInfo`, `PartialTermInfo`, or `TacticInfo`. `getDeclBodyInfos` does not validate either of these conditions. -/ def getDeclBodyInfos (t : InfoTree) : List (Syntax × ContextInfo × Info) := - t.foldInfoTree (init := []) fun ctx t acc => + t.foldInfoTree' (init := []) fun ctx t acc => match t with | .node (.ofCustomInfo i) body => Id.run do if i.value.typeName == ``Lean.Elab.Term.BodyInfo then diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 30a5204699122d..88c9fa60966521 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -173,8 +173,7 @@ open Linter in Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ def overlappingInstances : Linter where - run := - UnusedInstancesInType.withSetBoolOptionIn fun cmd => do + run := UnusedInstancesInType.withSetBoolOptionIn fun cmd => do unless getLinterValue linter.overlappingInstances (← getLinterOptions) do return -- Note: we don't break on errors; we want to lint even on partial declarations diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 1c5664002a5441..8b90f19b66c2bd 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -249,3 +249,18 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f example {α β} [B α β] [A' α] [B' α β] : True := trivial end parameters + +/-- +warning: Declaration `lt'.go` has overlapping instances: + +There are 2 `[DecidableEq α]` instances + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +def List.lt' {α} [DecidableEq α] (a b : List α) : Bool := + go a b +where + go [DecidableEq α] (_ _ : List α) : Bool := false From 70d42d4df91e90e79a29779a99f45edabf484625 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 17 Apr 2026 11:55:22 +0200 Subject: [PATCH 091/141] revert --- Mathlib/Lean/Elab/InfoTree.lean | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/Mathlib/Lean/Elab/InfoTree.lean b/Mathlib/Lean/Elab/InfoTree.lean index 93a1928362f4a6..9755b678eea600 100644 --- a/Mathlib/Lean/Elab/InfoTree.lean +++ b/Mathlib/Lean/Elab/InfoTree.lean @@ -130,27 +130,11 @@ def getDeclsByBody (t : InfoTree) : List Name := else decls | _ => decls -/-- A copy of `foldInfoTree`, but with the `@[specialize]` attribute. -/ -@[specialize] -partial def foldInfoTree' {α} (init : α) (f : ContextInfo → InfoTree → α → α) : InfoTree → α := - go none init -where - /-- `foldInfoTree.go` is like `foldInfoTree` but with an additional outer context parameter `ctx?`. -/ - @[specialize] - go ctx? a - | context ctx t => go (ctx.mergeIntoOuter? ctx?) a t - | t@(node i ts) => - let a := match ctx? with - | none => a - | some ctx => f ctx t a - ts.foldl (init := a) (go <| i.updateContext? ctx?) - | hole _ => a - /-- Gets the first child info of each `Lean.Elab.BodyInfo`, which should be the only child, and should be a `TermInfo`, `PartialTermInfo`, or `TacticInfo`. `getDeclBodyInfos` does not validate either of these conditions. -/ def getDeclBodyInfos (t : InfoTree) : List (Syntax × ContextInfo × Info) := - t.foldInfoTree' (init := []) fun ctx t acc => + t.foldInfoTree (init := []) fun ctx t acc => match t with | .node (.ofCustomInfo i) body => Id.run do if i.value.typeName == ``Lean.Elab.Term.BodyInfo then From 88dfef606eb6305082fbce597d87f1a8612e06f4 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 17 Apr 2026 12:06:33 +0200 Subject: [PATCH 092/141] avoid `runMetaMWithMessages` --- .../Tactic/Linter/OverlappingInstances.lean | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 88c9fa60966521..a4b266ffb63265 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -139,9 +139,15 @@ register_option linter.overlappingInstances : Bool := { descr := "enable the overlapping instances linter." } -/-- Creates a message describing the violations captured in `Overlaps`, assumed to be nonempty. -/ -def overlapsToMsg (overlaps : Std.HashMap Expr (Array FVarId)) (ctx : ContextInfo) : - MetaM MessageData := do +/-- Report a warning message if there are any overlapping instances in the local context. -/ +def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option Expr) : + IO (Option MessageData) := do + ctx.runMetaM lctx do + -- Add the hypotheses of the expected type to the local context. + expectedType?.elim id (forallTelescope · fun _ _ => ·) do + let overlaps ← findOverlappingDataInstances + if overlaps.isEmpty then + return none let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := overlaps.fold (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) -- Sort the suggestions in a somewhat fvarId-independent way @@ -166,7 +172,8 @@ def overlapsToMsg (overlaps : Std.HashMap Expr (Array FVarId)) (ctx : ContextInf msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." - addMessageContextFull msg + msg ← addMessageContextFull msg + return some msg open Linter in /-- @@ -179,12 +186,9 @@ def overlappingInstances : Linter where -- Note: we don't break on errors; we want to lint even on partial declarations for t in ← getInfoTrees do for (ref, ctx, info) in t.getDeclBodyInfos do - let some (lctx, remainingType?) := info.getLCtx? | continue - ctx.runMetaMWithMessages lctx do - remainingType?.elim id (forallTelescope · fun _ _ => ·) do - let overlaps ← findOverlappingDataInstances - unless overlaps.isEmpty do - logLint linter.overlappingInstances ref (← overlapsToMsg overlaps ctx) + let some (lctx, expectedType?) := info.getLCtx? | continue + let some msg ← runLinter ctx lctx expectedType? | continue + logLint linter.overlappingInstances ref msg initialize addLinter overlappingInstances From 74ba3a4c6980b2d88c902b4a40b12e1dee6b9266 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 17 Apr 2026 12:49:50 +0200 Subject: [PATCH 093/141] add trace nodes to help understand performance --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a4b266ffb63265..bfdb337fd4ce64 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -175,6 +175,8 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option msg ← addMessageContextFull msg return some msg +initialize registerTraceClass `overlappingInstances + open Linter in /-- Lints against data-carrying overlaps between instances in the local contexts of declarations. @@ -184,10 +186,12 @@ def overlappingInstances : Linter where unless getLinterValue linter.overlappingInstances (← getLinterOptions) do return -- Note: we don't break on errors; we want to lint even on partial declarations + withTraceNode `overlappingInstances (fun _ ↦ return "looking for a local context") do for t in ← getInfoTrees do for (ref, ctx, info) in t.getDeclBodyInfos do - let some (lctx, expectedType?) := info.getLCtx? | continue - let some msg ← runLinter ctx lctx expectedType? | continue + let some (lctx, expectedType?) := info.getLCtx? | pure () + withTraceNode `overlappingInstances (fun _ ↦ return m!"linting `{ctx.parentDecl?}`") do + let some msg ← runLinter ctx lctx expectedType? | pure () logLint linter.overlappingInstances ref msg initialize addLinter overlappingInstances From 211490f0fd60f6060889da4d14b933bf67cbba30 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 17 Apr 2026 14:44:35 +0200 Subject: [PATCH 094/141] update documentation and tests --- .../Tactic/Linter/OverlappingInstances.lean | 55 ++++++++----------- MathlibTest/OverlappingInstances.lean | 19 +++++-- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index bfdb337fd4ce64..c57a5377e97b74 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -16,36 +16,22 @@ public meta import Mathlib.Tactic.Linter.UnusedInstancesInType If the same data can be obtained from two different instances in the local context, we risk having non-defeq versions of that data. This situation, both for declarations and more broadly, is known -as an "instance diamond". This linter warns against declarations whose local contexts include -multiple versions of the same data. +as an "instance diamond". This linter warns against instance diamonds in local contexts. This is a syntax linter. It is run on partially and fully elaborated declarations. -Note that since all proofs of a given proposition are definitionally equal, multiple different ways -of obtaining instances of `Prop` classes pose no issue. So for `Prop` classes, this linter only -warns when the same class is assumed multiple times. +To find diamonds, we compute all data carrying parent classes of any given class. +For classes that are propositions or aren't structures, this returns the class itself. +If any of these classes is duplicated, we throw a warning. + +A common case where this linter may fire is if the same type class assumption is given in both a +`variable` statement and a declaration. This kind of variable shadowing does not actually produce +declarations with duplicate type class assumptions, but it is still not desirable. -Note that since this linter also warns against the trivial case of the same data-carrying instance -appearing twice, it warns against explicit local instance hypotheses which shadow `variable`s. -These may not influence the resulting type of the declaration, since Lean ignores unused instances, -but they are still duplicated in the local context while editing the body. ## TODO -- Improve performance. Currently running this linter in CI is prohibitively expensive. -- Expand to declarations without bodies (`structure`s/`class`es/`inductive`s etc.) -- The logging location for this linter could be improved. -- Currently it is possible to obtain a message which includes something of the following form: - ``` - • There are 2 instances of `[NonUnitalSemiring R]`. - • `[InvolutiveStar R]` is provided by both `[StarRing R]` and `[StarRing R]`. - ``` - This occurs because each of the two `StarRing`s relies on one of the two different - `NonUnitalSemiring` instances in the context, making them distinct (despite pretty-printing the - same way). However, their projection to `InvolutiveStar` no longer depends on this instance, and - thus coincides. The messages in this scenario could be improved. -- We could add hovers on the declaration name in messages. This is made tricky by the fact that it - conflicts with the auxdecl of the same name. +Support declarations without bodies (`structure`s/`class`es/`inductive`s etc.) -/ @@ -66,9 +52,15 @@ def eraseInstances (e : Expr) : MetaM Expr := do args := args.set! i default return mkAppN f args -/-- Compute the data projections of the class `cls`. -If `cls` is a `Prop` or a non-structure class, then return singleton array with just `cls`. -The results contain bound variables corresponding to the parameters of `cls`. -/ +/-- Compute the data carrying parent classes of `cls`. +This excludes parent classes that have a data carrying parent themselves. +The reason to exclude such classes is that if there is a duplication in such a class, +then there will necessarily also be a duplication in all of its parents. +If `cls` is a `Prop` or a non-structure class, this simply returns `#[cls]`. + +The resulting expressions contain bound variables that correspond to the parameters of `cls`. +The universe levels and bound variables need to be instantiated to get concrete data projections. +-/ partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do @@ -97,8 +89,8 @@ where /-- A cache for the result of `getAbstractDataProjections`. -/ initialize dataProjectionCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} -/-- Return the result of `getAbstractDataProjections` while using a cache. -To ensure soundness, we only use the cache for imported classes. -/ +/-- Return the result of `getAbstractDataProjections`, using a global cache. +To ensure soundness, the cache is only used for imported declarations. -/ def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do if (← getEnv).isImportedConst cls then if let some result := (← dataProjectionCache.get).find? cls then @@ -120,7 +112,7 @@ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId let projClasses ← forallTelescopeReducing (whnfType := true) type fun xs type ↦ do type.withApp fun f args ↦ do let .const cls us := f | - return #[] -- This happens when using `set_option checkBinderAnnotations false` + return #[] -- This can happen when using `set_option checkBinderAnnotations false` let info ← getConstInfo cls let projs ← getAbstractDataProjectionsCached cls projs.mapM fun proj ↦ @@ -143,14 +135,14 @@ register_option linter.overlappingInstances : Bool := { def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option Expr) : IO (Option MessageData) := do ctx.runMetaM lctx do - -- Add the hypotheses of the expected type to the local context. + -- Add the hypotheses of the expected type to the local context, as it may have more instances. expectedType?.elim id (forallTelescope · fun _ _ => ·) do let overlaps ← findOverlappingDataInstances if overlaps.isEmpty then return none let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := overlaps.fold (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) - -- Sort the suggestions in a somewhat fvarId-independent way + -- Sort the suggestions in a (somewhat) deterministic way. let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] for (fvars, overlaps) in sortedOverlaps do @@ -165,6 +157,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option -- Create a bulleted list if there are multiple messages, otherwise just a single line let declDescr ← if let some decl := ctx.parentDecl? then + -- It is slightly awkward to print `decl` because it is not in the current environment. pure m!"Declaration `{← unresolveNameGlobal decl}`" else pure "The current declaration" diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 8b90f19b66c2bd..25c10bba4788a3 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -97,7 +97,6 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f #guard_msgs in theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial --- Note that `[SubBar Nat]` is absent, as `[Bar Nat]` is already reported. /-- warning: Declaration `foo₅` has overlapping instances: @@ -128,10 +127,9 @@ private def foo [Add Nat] [Add Nat] : Bool := true end Foo -section classInductive +section duplicates -/-! Make sure we warn on duplicate inductive data-carrying inductive classes, even though these do -not have and cannot be structure projections. -/ +/-! Make sure we warn on duplicate inductive classes and duplicate `Prop` classes. -/ class inductive IndFoo where | mk₁ (n : Nat) | mk₂ (b : Bool) @@ -151,7 +149,6 @@ def indFoo [IndFoo] [IndFoo] : Bool := true class inductive IndFooProp : Prop where | mk₁ (n : Nat) | mk₂ (b : Bool) --- We also warn when there are duplicate `Prop` clases /-- warning: Declaration `indFooProp` has overlapping instances: @@ -164,7 +161,7 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f #guard_msgs in def indFooProp [IndFooProp] [IndFooProp] : Bool := true -end classInductive +end duplicates section instantiateMVars @@ -208,6 +205,8 @@ end setOptionIn namespace universes +/-! Test a projection that goes from `Type*` to `Sort*`. -/ + class A (α : Sort u) where class B (α : Type u) extends A α @@ -228,6 +227,8 @@ end universes namespace parameters +/-! Test a projection that changes the instance parameters. -/ + class A (α : Type*) where class A' (α : Type*) extends A α where @@ -250,6 +251,8 @@ example {α β} [B α β] [A' α] [B' α β] : True := trivial end parameters +/-! Test a `where` clause. -/ + /-- warning: Declaration `lt'.go` has overlapping instances: @@ -264,3 +267,7 @@ def List.lt' {α} [DecidableEq α] (a b : List α) : Bool := go a b where go [DecidableEq α] (_ _ : List α) : Bool := false + +-- Sadly, the linter does not work when the declaration doesn't have a body: + +class FooClass (α : Type) [Add α] [Add α] where From a61f8aa25988dcd84551bf72576488e1babac75a Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sun, 19 Apr 2026 23:42:25 +0200 Subject: [PATCH 095/141] feat: also warn against overlapping proofs --- .../Tactic/Linter/OverlappingInstances.lean | 58 ++++++++------- MathlibTest/OverlappingInstances.lean | 70 +++++++++++++------ 2 files changed, 79 insertions(+), 49 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index c57a5377e97b74..a24c4319ba3c18 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -41,8 +41,9 @@ meta section namespace Mathlib.Linter.OverlappingInstances -/-- Clear the instances from the given application. -This is used to deal with classes that have instance parameters. -/ +/-- Erase the instances from the given application. +We use this for classes with instance parameters, +in order to ignore the instances when checking for equality. -/ def eraseInstances (e : Expr) : MetaM Expr := do e.withApp fun f args ↦ do let finfo ← getFunInfo f @@ -52,16 +53,16 @@ def eraseInstances (e : Expr) : MetaM Expr := do args := args.set! i default return mkAppN f args -/-- Compute the data carrying parent classes of `cls`. -This excludes parent classes that have a data carrying parent themselves. -The reason to exclude such classes is that if there is a duplication in such a class, +/-- +Compute the parent classes of `cls`. This excludes parents that have parents themselves. +The reason to exclude these is that if there is a duplication in such a class, then there will necessarily also be a duplication in all of its parents. If `cls` is a `Prop` or a non-structure class, this simply returns `#[cls]`. The resulting expressions contain bound variables that correspond to the parameters of `cls`. The universe levels and bound variables need to be instantiated to get concrete data projections. -/ -partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do +partial def getAbstractParents (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do withLocalDeclD `self (mkAppN (.const cls (cinfo.levelParams.map .param)) xs) fun inst ↦ do @@ -75,35 +76,34 @@ where if let some info := getStructureInfo? (← getEnv) cls then let .const _ us := type.getAppFn | panic! s!"`{inst} is not an instance" for info in info.parentInfo do + anyParent := true let parent := info.structName if (← get).contains parent then continue modify (·.insert parent) - if (← getConstInfo parent).type.getForallBody.isProp then continue let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst acc ← go parent proj acc xs - anyParent := true if !anyParent then acc := acc.push (← eraseInstances (type.abstract xs)) return acc -/-- A cache for the result of `getAbstractDataProjections`. -/ -initialize dataProjectionCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} +/-- A cache for the result of `getAbstractParents`. -/ +initialize abstractParentsCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} -/-- Return the result of `getAbstractDataProjections`, using a global cache. +/-- Return the result of `getAbstractParents`, using a global cache. To ensure soundness, the cache is only used for imported declarations. -/ -def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do +def getAbstractParentsCached (cls : Name) : CoreM (Array Expr) := do if (← getEnv).isImportedConst cls then - if let some result := (← dataProjectionCache.get).find? cls then + if let some result := (← abstractParentsCache.get).find? cls then return result else - let result ← getAbstractDataProjections cls - dataProjectionCache.modify (·.insert cls result) + let result ← getAbstractParents cls + abstractParentsCache.modify (·.insert cls result) return result else - getAbstractDataProjections cls + getAbstractParents cls -/-- Find classes for which multiple different instances can be synthesized in the local context. -/ -partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId)) := do +/-- Find classes for which multiple different local instances can be synthesized. -/ +partial def findOverlappingInstances : MetaM (Std.HashMap Expr (Array FVarId)) := do let mut overlaps : Std.HashMap Expr (Array FVarId) := {} let mut encountered : Std.HashMap Expr FVarId := {} for decl in ← getLCtx do @@ -114,7 +114,7 @@ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId let .const cls us := f | return #[] -- This can happen when using `set_option checkBinderAnnotations false` let info ← getConstInfo cls - let projs ← getAbstractDataProjectionsCached cls + let projs ← getAbstractParentsCached cls projs.mapM fun proj ↦ mkForallFVars xs <| (proj.instantiateLevelParams info.levelParams us).instantiateRev args @@ -125,19 +125,19 @@ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId encountered := encountered.insert projCls decl.fvarId return overlaps -/-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ +/-- Lints against overlaps between instances in the local contexts of declarations. -/ register_option linter.overlappingInstances : Bool := { defValue := true descr := "enable the overlapping instances linter." } /-- Report a warning message if there are any overlapping instances in the local context. -/ -def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option Expr) : - IO (Option MessageData) := do +def reportOverlappingInstances (ctx : ContextInfo) (lctx : LocalContext) + (expectedType? : Option Expr) : IO (Option MessageData) := do ctx.runMetaM lctx do -- Add the hypotheses of the expected type to the local context, as it may have more instances. expectedType?.elim id (forallTelescope · fun _ _ => ·) do - let overlaps ← findOverlappingDataInstances + let overlaps ← findOverlappingInstances if overlaps.isEmpty then return none let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := @@ -145,7 +145,9 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option -- Sort the suggestions in a (somewhat) deterministic way. let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] + let mut isOnlyProp := true for (fvars, overlaps) in sortedOverlaps do + isOnlyProp ← pure isOnlyProp <&&> overlaps.allM isProp let parents ← fvars.mapM (do instantiateMVars <| ← ·.getType) if parents.all (· == parents[0]!) then msgs := msgs.push <| m!"There are {parents.size} `{.sbracket parents[0]!}` instances" @@ -153,7 +155,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let parents := parents.map (m!"`{.sbracket ·}`") let children := overlaps.map fun overlap => m!"`{overlap}`" msgs := msgs.push <| - m!"{.andList parents.toList} give conflicting instances of {.andList children.toList}." + m!"{.andList parents.toList} each give an instance of {.andList children.toList}." -- Create a bulleted list if there are multiple messages, otherwise just a single line let declDescr ← if let some decl := ctx.parentDecl? then @@ -164,7 +166,11 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let mut msg := m!"{declDescr} has overlapping instances:" msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") - msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." + if isOnlyProp then + msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." + else + msg := msg ++ m!"\n\nThese conflicting instances create a local 'diamond', \ + which can lead to unexpected errors." msg ← addMessageContextFull msg return some msg @@ -184,7 +190,7 @@ def overlappingInstances : Linter where for (ref, ctx, info) in t.getDeclBodyInfos do let some (lctx, expectedType?) := info.getLCtx? | pure () withTraceNode `overlappingInstances (fun _ ↦ return m!"linting `{ctx.parentDecl?}`") do - let some msg ← runLinter ctx lctx expectedType? | pure () + let some msg ← reportOverlappingInstances ctx lctx expectedType? | pure () logLint linter.overlappingInstances ref msg initialize addLinter overlappingInstances diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 25c10bba4788a3..db4d25566660d8 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -34,7 +34,7 @@ warning: Declaration `foo` has overlapping instances: There are 4 `[Add Nat]` instances -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -47,9 +47,9 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @ +3:68...+4:12 warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` each give an instance of `SubBar Nat`. -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -63,9 +63,9 @@ set_option linter.overlappingInstances true in warning: Declaration `foo₂` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` each give an instance of `SubBar Nat`. -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -77,7 +77,7 @@ warning: Declaration `foo₃` has overlapping instances: There are 2 `[FooBarBaz Nat]` instances -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -88,9 +88,9 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true warning: Declaration `foo₄` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `SubBar Nat`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` each give an instance of `SubBar Nat`. -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -100,9 +100,9 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- warning: Declaration `foo₅` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `Baz Nat` and `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` each give an instance of `Baz Nat` and `SubBar Nat`. -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -118,7 +118,7 @@ warning: Declaration `foo` has overlapping instances: There are 2 `[Add Nat]` instances -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -127,9 +127,9 @@ private def foo [Add Nat] [Add Nat] : Bool := true end Foo -section duplicates +section Inductive -/-! Make sure we warn on duplicate inductive classes and duplicate `Prop` classes. -/ +/-! Make sure we warn on duplicate inductive classes. -/ class inductive IndFoo where | mk₁ (n : Nat) | mk₂ (b : Bool) @@ -139,13 +139,36 @@ warning: Declaration `indFoo` has overlapping instances: There are 2 `[IndFoo]` instances -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in def indFoo [IndFoo] [IndFoo] : Bool := true +end Inductive + +section prop + + +class SubBarProp (α : Prop) : Prop where + a' : α + +class BarProp (α : Prop) : Prop extends SubBarProp α where + a : α + +/-- +warning: Declaration `_example` has overlapping instances: + +`[SubBarProp α]` and `[BarProp α]` each give an instance of `SubBarProp α`. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example (α : Prop) [SubBarProp α] [BarProp α] : True := trivial + class inductive IndFooProp : Prop where | mk₁ (n : Nat) | mk₂ (b : Bool) @@ -161,7 +184,7 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f #guard_msgs in def indFooProp [IndFooProp] [IndFooProp] : Bool := true -end duplicates +end prop section instantiateMVars @@ -172,7 +195,7 @@ warning: Declaration `needsInstantiateMVars` has overlapping instances: There are 2 `[Repr α]` instances -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -193,7 +216,7 @@ warning: Declaration `fooSomething` has overlapping instances: There are 4 `[Add Nat]` instances -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -214,9 +237,9 @@ class B (α : Type u) extends A α /-- warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` give conflicting instances of `A α`. +`[B α]` and `[A α]` each give an instance of `A α`. -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -240,9 +263,9 @@ class B' (α β : Type*) [A' α] extends B α β where /-- warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` give conflicting instances of `B α β`. +`[B α β]` and `[B' α β]` each give an instance of `B α β`. -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -254,15 +277,16 @@ end parameters /-! Test a `where` clause. -/ /-- +@ +4:46...51 warning: Declaration `lt'.go` has overlapping instances: There are 2 `[DecidableEq α]` instances -Consider choosing different instance hypotheses. +These conflicting instances create a local 'diamond', which can lead to unexpected errors. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ -#guard_msgs in +#guard_msgs (positions := true) in def List.lt' {α} [DecidableEq α] (a b : List α) : Bool := go a b where From 4f16178a4df4740b833c21db31c8e96735d24255 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Mon, 20 Apr 2026 11:51:55 +0200 Subject: [PATCH 096/141] hard-code an exception for `Nontrivial` --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a24c4319ba3c18..a4f01081ff9ba1 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -76,8 +76,10 @@ where if let some info := getStructureInfo? (← getEnv) cls then let .const _ us := type.getAppFn | panic! s!"`{inst} is not an instance" for info in info.parentInfo do - anyParent := true let parent := info.structName + -- hard-code an exception for `Nontrivial`, which commonly has multiple local instances. + if parent matches `Nontrivial then continue + anyParent := true if (← get).contains parent then continue modify (·.insert parent) let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst From 2b9d82e0d8abd9bf7269131276203a5647b608f3 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 23 Apr 2026 19:02:29 +0100 Subject: [PATCH 097/141] Revert "hard-code an exception for `Nontrivial`" This reverts commit 4f16178a4df4740b833c21db31c8e96735d24255. --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a4f01081ff9ba1..a24c4319ba3c18 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -76,10 +76,8 @@ where if let some info := getStructureInfo? (← getEnv) cls then let .const _ us := type.getAppFn | panic! s!"`{inst} is not an instance" for info in info.parentInfo do - let parent := info.structName - -- hard-code an exception for `Nontrivial`, which commonly has multiple local instances. - if parent matches `Nontrivial then continue anyParent := true + let parent := info.structName if (← get).contains parent then continue modify (·.insert parent) let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst From f98207f71b91bf0ff1f88670d20857d6875a0fa3 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 23 Apr 2026 19:02:50 +0100 Subject: [PATCH 098/141] Revert "feat: also warn against overlapping proofs" This reverts commit a61f8aa25988dcd84551bf72576488e1babac75a. --- .../Tactic/Linter/OverlappingInstances.lean | 58 +++++++-------- MathlibTest/OverlappingInstances.lean | 70 ++++++------------- 2 files changed, 49 insertions(+), 79 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a24c4319ba3c18..c57a5377e97b74 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -41,9 +41,8 @@ meta section namespace Mathlib.Linter.OverlappingInstances -/-- Erase the instances from the given application. -We use this for classes with instance parameters, -in order to ignore the instances when checking for equality. -/ +/-- Clear the instances from the given application. +This is used to deal with classes that have instance parameters. -/ def eraseInstances (e : Expr) : MetaM Expr := do e.withApp fun f args ↦ do let finfo ← getFunInfo f @@ -53,16 +52,16 @@ def eraseInstances (e : Expr) : MetaM Expr := do args := args.set! i default return mkAppN f args -/-- -Compute the parent classes of `cls`. This excludes parents that have parents themselves. -The reason to exclude these is that if there is a duplication in such a class, +/-- Compute the data carrying parent classes of `cls`. +This excludes parent classes that have a data carrying parent themselves. +The reason to exclude such classes is that if there is a duplication in such a class, then there will necessarily also be a duplication in all of its parents. If `cls` is a `Prop` or a non-structure class, this simply returns `#[cls]`. The resulting expressions contain bound variables that correspond to the parameters of `cls`. The universe levels and bound variables need to be instantiated to get concrete data projections. -/ -partial def getAbstractParents (cls : Name) : CoreM (Array Expr) := do +partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do withLocalDeclD `self (mkAppN (.const cls (cinfo.levelParams.map .param)) xs) fun inst ↦ do @@ -76,34 +75,35 @@ where if let some info := getStructureInfo? (← getEnv) cls then let .const _ us := type.getAppFn | panic! s!"`{inst} is not an instance" for info in info.parentInfo do - anyParent := true let parent := info.structName if (← get).contains parent then continue modify (·.insert parent) + if (← getConstInfo parent).type.getForallBody.isProp then continue let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst acc ← go parent proj acc xs + anyParent := true if !anyParent then acc := acc.push (← eraseInstances (type.abstract xs)) return acc -/-- A cache for the result of `getAbstractParents`. -/ -initialize abstractParentsCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} +/-- A cache for the result of `getAbstractDataProjections`. -/ +initialize dataProjectionCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} -/-- Return the result of `getAbstractParents`, using a global cache. +/-- Return the result of `getAbstractDataProjections`, using a global cache. To ensure soundness, the cache is only used for imported declarations. -/ -def getAbstractParentsCached (cls : Name) : CoreM (Array Expr) := do +def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do if (← getEnv).isImportedConst cls then - if let some result := (← abstractParentsCache.get).find? cls then + if let some result := (← dataProjectionCache.get).find? cls then return result else - let result ← getAbstractParents cls - abstractParentsCache.modify (·.insert cls result) + let result ← getAbstractDataProjections cls + dataProjectionCache.modify (·.insert cls result) return result else - getAbstractParents cls + getAbstractDataProjections cls -/-- Find classes for which multiple different local instances can be synthesized. -/ -partial def findOverlappingInstances : MetaM (Std.HashMap Expr (Array FVarId)) := do +/-- Find classes for which multiple different instances can be synthesized in the local context. -/ +partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId)) := do let mut overlaps : Std.HashMap Expr (Array FVarId) := {} let mut encountered : Std.HashMap Expr FVarId := {} for decl in ← getLCtx do @@ -114,7 +114,7 @@ partial def findOverlappingInstances : MetaM (Std.HashMap Expr (Array FVarId)) : let .const cls us := f | return #[] -- This can happen when using `set_option checkBinderAnnotations false` let info ← getConstInfo cls - let projs ← getAbstractParentsCached cls + let projs ← getAbstractDataProjectionsCached cls projs.mapM fun proj ↦ mkForallFVars xs <| (proj.instantiateLevelParams info.levelParams us).instantiateRev args @@ -125,19 +125,19 @@ partial def findOverlappingInstances : MetaM (Std.HashMap Expr (Array FVarId)) : encountered := encountered.insert projCls decl.fvarId return overlaps -/-- Lints against overlaps between instances in the local contexts of declarations. -/ +/-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ register_option linter.overlappingInstances : Bool := { defValue := true descr := "enable the overlapping instances linter." } /-- Report a warning message if there are any overlapping instances in the local context. -/ -def reportOverlappingInstances (ctx : ContextInfo) (lctx : LocalContext) - (expectedType? : Option Expr) : IO (Option MessageData) := do +def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option Expr) : + IO (Option MessageData) := do ctx.runMetaM lctx do -- Add the hypotheses of the expected type to the local context, as it may have more instances. expectedType?.elim id (forallTelescope · fun _ _ => ·) do - let overlaps ← findOverlappingInstances + let overlaps ← findOverlappingDataInstances if overlaps.isEmpty then return none let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := @@ -145,9 +145,7 @@ def reportOverlappingInstances (ctx : ContextInfo) (lctx : LocalContext) -- Sort the suggestions in a (somewhat) deterministic way. let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] - let mut isOnlyProp := true for (fvars, overlaps) in sortedOverlaps do - isOnlyProp ← pure isOnlyProp <&&> overlaps.allM isProp let parents ← fvars.mapM (do instantiateMVars <| ← ·.getType) if parents.all (· == parents[0]!) then msgs := msgs.push <| m!"There are {parents.size} `{.sbracket parents[0]!}` instances" @@ -155,7 +153,7 @@ def reportOverlappingInstances (ctx : ContextInfo) (lctx : LocalContext) let parents := parents.map (m!"`{.sbracket ·}`") let children := overlaps.map fun overlap => m!"`{overlap}`" msgs := msgs.push <| - m!"{.andList parents.toList} each give an instance of {.andList children.toList}." + m!"{.andList parents.toList} give conflicting instances of {.andList children.toList}." -- Create a bulleted list if there are multiple messages, otherwise just a single line let declDescr ← if let some decl := ctx.parentDecl? then @@ -166,11 +164,7 @@ def reportOverlappingInstances (ctx : ContextInfo) (lctx : LocalContext) let mut msg := m!"{declDescr} has overlapping instances:" msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") - if isOnlyProp then - msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." - else - msg := msg ++ m!"\n\nThese conflicting instances create a local 'diamond', \ - which can lead to unexpected errors." + msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." msg ← addMessageContextFull msg return some msg @@ -190,7 +184,7 @@ def overlappingInstances : Linter where for (ref, ctx, info) in t.getDeclBodyInfos do let some (lctx, expectedType?) := info.getLCtx? | pure () withTraceNode `overlappingInstances (fun _ ↦ return m!"linting `{ctx.parentDecl?}`") do - let some msg ← reportOverlappingInstances ctx lctx expectedType? | pure () + let some msg ← runLinter ctx lctx expectedType? | pure () logLint linter.overlappingInstances ref msg initialize addLinter overlappingInstances diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index db4d25566660d8..25c10bba4788a3 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -34,7 +34,7 @@ warning: Declaration `foo` has overlapping instances: There are 4 `[Add Nat]` instances -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -47,9 +47,9 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @ +3:68...+4:12 warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` each give an instance of `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -63,9 +63,9 @@ set_option linter.overlappingInstances true in warning: Declaration `foo₂` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` each give an instance of `SubBar Nat`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -77,7 +77,7 @@ warning: Declaration `foo₃` has overlapping instances: There are 2 `[FooBarBaz Nat]` instances -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -88,9 +88,9 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true warning: Declaration `foo₄` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` each give an instance of `SubBar Nat`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `SubBar Nat`. -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -100,9 +100,9 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- warning: Declaration `foo₅` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` each give an instance of `Baz Nat` and `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `Baz Nat` and `SubBar Nat`. -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -118,7 +118,7 @@ warning: Declaration `foo` has overlapping instances: There are 2 `[Add Nat]` instances -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -127,9 +127,9 @@ private def foo [Add Nat] [Add Nat] : Bool := true end Foo -section Inductive +section duplicates -/-! Make sure we warn on duplicate inductive classes. -/ +/-! Make sure we warn on duplicate inductive classes and duplicate `Prop` classes. -/ class inductive IndFoo where | mk₁ (n : Nat) | mk₂ (b : Bool) @@ -139,35 +139,12 @@ warning: Declaration `indFoo` has overlapping instances: There are 2 `[IndFoo]` instances -These conflicting instances create a local 'diamond', which can lead to unexpected errors. - -Note: This linter can be disabled with `set_option linter.overlappingInstances false` --/ -#guard_msgs in -def indFoo [IndFoo] [IndFoo] : Bool := true - -end Inductive - -section prop - - -class SubBarProp (α : Prop) : Prop where - a' : α - -class BarProp (α : Prop) : Prop extends SubBarProp α where - a : α - -/-- -warning: Declaration `_example` has overlapping instances: - -`[SubBarProp α]` and `[BarProp α]` each give an instance of `SubBarProp α`. - Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in -example (α : Prop) [SubBarProp α] [BarProp α] : True := trivial +def indFoo [IndFoo] [IndFoo] : Bool := true class inductive IndFooProp : Prop where | mk₁ (n : Nat) | mk₂ (b : Bool) @@ -184,7 +161,7 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f #guard_msgs in def indFooProp [IndFooProp] [IndFooProp] : Bool := true -end prop +end duplicates section instantiateMVars @@ -195,7 +172,7 @@ warning: Declaration `needsInstantiateMVars` has overlapping instances: There are 2 `[Repr α]` instances -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -216,7 +193,7 @@ warning: Declaration `fooSomething` has overlapping instances: There are 4 `[Add Nat]` instances -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -237,9 +214,9 @@ class B (α : Type u) extends A α /-- warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` each give an instance of `A α`. +`[B α]` and `[A α]` give conflicting instances of `A α`. -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -263,9 +240,9 @@ class B' (α β : Type*) [A' α] extends B α β where /-- warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` each give an instance of `B α β`. +`[B α β]` and `[B' α β]` give conflicting instances of `B α β`. -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -277,16 +254,15 @@ end parameters /-! Test a `where` clause. -/ /-- -@ +4:46...51 warning: Declaration `lt'.go` has overlapping instances: There are 2 `[DecidableEq α]` instances -These conflicting instances create a local 'diamond', which can lead to unexpected errors. +Consider choosing different instance hypotheses. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ -#guard_msgs (positions := true) in +#guard_msgs in def List.lt' {α} [DecidableEq α] (a b : List α) : Bool := go a b where From d64f825805a20b387febfd4d8699a80d7575d92d Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Tue, 28 Apr 2026 22:40:17 +0100 Subject: [PATCH 099/141] review suggestions --- .../Tactic/Linter/OverlappingInstances.lean | 50 ++++++++++--------- MathlibTest/OverlappingInstances.lean | 49 +++++++++++------- 2 files changed, 58 insertions(+), 41 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index c57a5377e97b74..48528c1f595a5e 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -7,7 +7,7 @@ module public meta import Lean.Elab.Command public meta import Mathlib.Lean.ContextInfo -public meta import Mathlib.Lean.Elab.InfoTree +public meta import Batteries.Lean.Position public meta import Mathlib.Tactic.Linter.UnusedInstancesInType @@ -42,7 +42,9 @@ meta section namespace Mathlib.Linter.OverlappingInstances /-- Clear the instances from the given application. -This is used to deal with classes that have instance parameters. -/ +This is used to deal with classes that have instance parameters. +For example, if you have a local instance of `ContinuousAdd α` and `IsTopologicalAddGroup α`, +then the two `ContinuousAdd α` instances may have slightly different `[Add α]` arguments. -/ def eraseInstances (e : Expr) : MetaM Expr := do e.withApp fun f args ↦ do let finfo ← getFunInfo f @@ -59,8 +61,7 @@ then there will necessarily also be a duplication in all of its parents. If `cls` is a `Prop` or a non-structure class, this simply returns `#[cls]`. The resulting expressions contain bound variables that correspond to the parameters of `cls`. -The universe levels and bound variables need to be instantiated to get concrete data projections. --/ +The universe levels and bound variables need to be instantiated to get concrete data projections. -/ partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do @@ -95,17 +96,17 @@ def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do if (← getEnv).isImportedConst cls then if let some result := (← dataProjectionCache.get).find? cls then return result - else let result ← getAbstractDataProjections cls dataProjectionCache.modify (·.insert cls result) return result else getAbstractDataProjections cls -/-- Find classes for which multiple different instances can be synthesized in the local context. -/ -partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId)) := do - let mut overlaps : Std.HashMap Expr (Array FVarId) := {} - let mut encountered : Std.HashMap Expr FVarId := {} +/-- Find classes for which multiple different instances can be synthesized in the local context. +The result maps classes to the (at least 2) local instances that generate them. -/ +partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do + let mut overlaps : ExprMap (Array FVarId) := {} + let mut encountered : ExprMap FVarId := {} for decl in ← getLCtx do if decl.binderInfo.isInstImplicit then let type ← instantiateMVars decl.type @@ -113,11 +114,10 @@ partial def findOverlappingDataInstances : MetaM (Std.HashMap Expr (Array FVarId type.withApp fun f args ↦ do let .const cls us := f | return #[] -- This can happen when using `set_option checkBinderAnnotations false` - let info ← getConstInfo cls + let levelParams := (← getConstInfo cls).levelParams let projs ← getAbstractDataProjectionsCached cls projs.mapM fun proj ↦ - mkForallFVars xs <| - (proj.instantiateLevelParams info.levelParams us).instantiateRev args + mkForallFVars xs <| (proj.instantiateLevelParams levelParams us).instantiateRev args for projCls in projClasses do if let some fvarId' := encountered[projCls]? then overlaps := overlaps.alter projCls (·.getD #[fvarId'] |>.push decl.fvarId) @@ -146,27 +146,26 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] for (fvars, overlaps) in sortedOverlaps do - let parents ← fvars.mapM (do instantiateMVars <| ← ·.getType) - if parents.all (· == parents[0]!) then - msgs := msgs.push <| m!"There are {parents.size} `{.sbracket parents[0]!}` instances" + let fvarTypes ← fvars.mapM (do instantiateMVars <| ← ·.getType) + if fvarTypes.all (· == fvarTypes[0]!) then + msgs := msgs.push <| m!"There are {fvarTypes.size} `{.sbracket fvarTypes[0]!}` instances." else - let parents := parents.map (m!"`{.sbracket ·}`") - let children := overlaps.map fun overlap => m!"`{overlap}`" - msgs := msgs.push <| - m!"{.andList parents.toList} give conflicting instances of {.andList children.toList}." + let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") + let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") + msgs := msgs.push <| m!"{fvarTypes} give conflicting instances of {overlaps}." -- Create a bulleted list if there are multiple messages, otherwise just a single line let declDescr ← if let some decl := ctx.parentDecl? then - -- It is slightly awkward to print `decl` because it is not in the current environment. - pure m!"Declaration `{← unresolveNameGlobal decl}`" + -- Use `addMessageContextPartial` to clear the local context, + -- so as to avoid a name clash with the recursive auxiliary hypothesis of the same name. + pure m!"Declaration `{← addMessageContextPartial (.ofConstName decl)}`" else pure "The current declaration" let mut msg := m!"{declDescr} has overlapping instances:" msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." - msg ← addMessageContextFull msg - return some msg + addMessageContextFull msg initialize registerTraceClass `overlappingInstances @@ -185,6 +184,11 @@ def overlappingInstances : Linter where let some (lctx, expectedType?) := info.getLCtx? | pure () withTraceNode `overlappingInstances (fun _ ↦ return m!"linting `{ctx.parentDecl?}`") do let some msg ← runLinter ctx lctx expectedType? | pure () + /- Log the warning from the declaration's selection range (usually the declaration name, + or `instance`) to the body if possible. This underlines the hypotheses and type, + and makes the warning visible in the infoview when the cursor is within the body. -/ + let declRange? ← ctx.parentDecl?.bindM findDeclarationSyntaxRange? + let ref := declRange?.elim ref (mkNullNode #[.ofRange ·, ref]) logLint linter.overlappingInstances ref msg initialize addLinter overlappingInstances diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 25c10bba4788a3..47b2124b089eca 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -32,7 +32,7 @@ inst✝¹ inst✝ : Add Nat --- warning: Declaration `foo` has overlapping instances: -There are 4 `[Add Nat]` instances +There are 4 `[Add Nat]` instances. Consider choosing different instance hypotheses. @@ -44,10 +44,10 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- -@ +3:68...+4:12 +@ +3:21...+4:12 warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -62,8 +62,8 @@ set_option linter.overlappingInstances true in /-- warning: Declaration `foo₂` has overlapping instances: -• There are 2 `[FooBarBaz Nat]` instances -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `SubBar Nat`. +• There are 2 `[FooBarBaz Nat]` instances. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -75,7 +75,7 @@ def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- warning: Declaration `foo₃` has overlapping instances: -There are 2 `[FooBarBaz Nat]` instances +There are 2 `[FooBarBaz Nat]` instances. Consider choosing different instance hypotheses. @@ -87,8 +87,8 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- warning: Declaration `foo₄` has overlapping instances: -• There are 2 `[FooBarBaz Nat]` instances -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `SubBar Nat`. +• There are 2 `[FooBarBaz Nat]` instances. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -100,7 +100,7 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- warning: Declaration `foo₅` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `Baz Nat` and `SubBar Nat`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]` and `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -109,6 +109,19 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f #guard_msgs in lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial +/-- +warning: Declaration `foo₆` has overlapping instances: + +• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +lemma foo₆ [FooBarBaz Nat] [FooBarBaz' Nat] [FooBarBaq Nat] : True := trivial + namespace Foo /-! Test unresolving name (`foo`, not `Foo.foo` or `_private...foo`) -/ @@ -116,7 +129,7 @@ namespace Foo /-- warning: Declaration `foo` has overlapping instances: -There are 2 `[Add Nat]` instances +There are 2 `[Add Nat]` instances. Consider choosing different instance hypotheses. @@ -137,7 +150,7 @@ class inductive IndFoo where /-- warning: Declaration `indFoo` has overlapping instances: -There are 2 `[IndFoo]` instances +There are 2 `[IndFoo]` instances. Consider choosing different instance hypotheses. @@ -152,7 +165,7 @@ class inductive IndFooProp : Prop where /-- warning: Declaration `indFooProp` has overlapping instances: -There are 2 `[IndFooProp]` instances +There are 2 `[IndFooProp]` instances. Consider choosing different instance hypotheses. @@ -170,7 +183,7 @@ variable {α : Type*} [Repr α] /-- warning: Declaration `needsInstantiateMVars` has overlapping instances: -There are 2 `[Repr α]` instances +There are 2 `[Repr α]` instances. Consider choosing different instance hypotheses. @@ -191,7 +204,7 @@ set_option linter.overlappingInstances false /-- warning: Declaration `fooSomething` has overlapping instances: -There are 4 `[Add Nat]` instances +There are 4 `[Add Nat]` instances. Consider choosing different instance hypotheses. @@ -214,7 +227,7 @@ class B (α : Type u) extends A α /-- warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` give conflicting instances of `A α`. +`[B α]` and `[A α]` give conflicting instances of `[A α]`. Consider choosing different instance hypotheses. @@ -240,7 +253,7 @@ class B' (α β : Type*) [A' α] extends B α β where /-- warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` give conflicting instances of `B α β`. +`[B α β]` and `[B' α β]` give conflicting instances of `[B α β]`. Consider choosing different instance hypotheses. @@ -254,9 +267,9 @@ end parameters /-! Test a `where` clause. -/ /-- -warning: Declaration `lt'.go` has overlapping instances: +warning: Declaration `List.lt'.go` has overlapping instances: -There are 2 `[DecidableEq α]` instances +There are 2 `[DecidableEq α]` instances. Consider choosing different instance hypotheses. From 14264d00292f52f038d07c123dd8b4107fd1e9c9 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Tue, 28 Apr 2026 23:14:03 +0100 Subject: [PATCH 100/141] more comments --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 48528c1f595a5e..50258e42f9db96 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -105,7 +105,10 @@ def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do /-- Find classes for which multiple different instances can be synthesized in the local context. The result maps classes to the (at least 2) local instances that generate them. -/ partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do + -- Maps a class to the collection of local instances that overlap on it. + -- This only includes overlaps of at least 2 local instances. let mut overlaps : ExprMap (Array FVarId) := {} + -- Maps a class to the first local instance that produces an instance of it. let mut encountered : ExprMap FVarId := {} for decl in ← getLCtx do if decl.binderInfo.isInstImplicit then From 3201d6e74f6996514b211949d1811d95fbe36363 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Tue, 28 Apr 2026 23:20:06 +0100 Subject: [PATCH 101/141] let's try `Std.TreeMap` for benchmarking --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 50258e42f9db96..2781325b874d57 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -104,12 +104,13 @@ def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do /-- Find classes for which multiple different instances can be synthesized in the local context. The result maps classes to the (at least 2) local instances that generate them. -/ -partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do +partial def findOverlappingDataInstances : + MetaM (Std.TreeMap Expr (Array FVarId) Expr.quickComp) := do -- Maps a class to the collection of local instances that overlap on it. -- This only includes overlaps of at least 2 local instances. - let mut overlaps : ExprMap (Array FVarId) := {} + let mut overlaps : Std.TreeMap Expr (Array FVarId) Expr.quickComp := {} -- Maps a class to the first local instance that produces an instance of it. - let mut encountered : ExprMap FVarId := {} + let mut encountered : Std.TreeMap Expr FVarId Expr.quickComp := {} for decl in ← getLCtx do if decl.binderInfo.isInstImplicit then let type ← instantiateMVars decl.type @@ -144,7 +145,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option if overlaps.isEmpty then return none let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := - overlaps.fold (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) + overlaps.foldl (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) -- Sort the suggestions in a (somewhat) deterministic way. let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] From 061ce7f65b00093d4ba8d85dfa60b9708615cfea Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Tue, 28 Apr 2026 23:29:10 +0100 Subject: [PATCH 102/141] fix new violation --- Mathlib/Topology/Connected/PathConnected.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Topology/Connected/PathConnected.lean b/Mathlib/Topology/Connected/PathConnected.lean index 46e8b52415fa8e..4f062ef7f1aec4 100644 --- a/Mathlib/Topology/Connected/PathConnected.lean +++ b/Mathlib/Topology/Connected/PathConnected.lean @@ -565,7 +565,7 @@ variable [PathConnectedSpace X] def somePath (x y : X) : Path x y := Nonempty.some (joined x y) -instance [PathConnectedSpace X] : Subsingleton (ZerothHomotopy X) := +instance : Subsingleton (ZerothHomotopy X) := (pathConnectedSpace_iff_zerothHomotopy.1 inferInstance).2 end PathConnectedSpace From 05f575c08f690b3905f0fd30168c90cda52d9f94 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Tue, 28 Apr 2026 23:46:11 +0100 Subject: [PATCH 103/141] move comment to correct place --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 2781325b874d57..51f4dc78abe06c 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -157,7 +157,6 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") msgs := msgs.push <| m!"{fvarTypes} give conflicting instances of {overlaps}." - -- Create a bulleted list if there are multiple messages, otherwise just a single line let declDescr ← if let some decl := ctx.parentDecl? then -- Use `addMessageContextPartial` to clear the local context, @@ -166,6 +165,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option else pure "The current declaration" let mut msg := m!"{declDescr} has overlapping instances:" + -- Create a bulleted list if there are multiple messages, otherwise just a single line msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." From cf04252c088401a6a15b945b91e58b69d8fa6ab5 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Wed, 29 Apr 2026 00:08:40 +0100 Subject: [PATCH 104/141] revert to `exprMap` --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 51f4dc78abe06c..f3907fbcae5876 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -104,13 +104,12 @@ def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do /-- Find classes for which multiple different instances can be synthesized in the local context. The result maps classes to the (at least 2) local instances that generate them. -/ -partial def findOverlappingDataInstances : - MetaM (Std.TreeMap Expr (Array FVarId) Expr.quickComp) := do +partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do -- Maps a class to the collection of local instances that overlap on it. -- This only includes overlaps of at least 2 local instances. - let mut overlaps : Std.TreeMap Expr (Array FVarId) Expr.quickComp := {} + let mut overlaps : ExprMap (Array FVarId) := {} -- Maps a class to the first local instance that produces an instance of it. - let mut encountered : Std.TreeMap Expr FVarId Expr.quickComp := {} + let mut encountered : ExprMap FVarId := {} for decl in ← getLCtx do if decl.binderInfo.isInstImplicit then let type ← instantiateMVars decl.type From ad109534577a397a2c30f04ad374eecdfee6e947 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Wed, 29 Apr 2026 00:14:57 +0100 Subject: [PATCH 105/141] . --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index f3907fbcae5876..5237ea18aee64e 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -144,7 +144,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option if overlaps.isEmpty then return none let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := - overlaps.foldl (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) + overlaps.fold (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) -- Sort the suggestions in a (somewhat) deterministic way. let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] From 4e2b7a2a725732cbb77f27c0e4fabe70ea710d24 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 30 Apr 2026 15:57:20 +0100 Subject: [PATCH 106/141] attempt 2 for linting against Prop classes --- .../Tactic/Linter/OverlappingInstances.lean | 31 ++++++++++--------- MathlibTest/OverlappingInstances.lean | 16 +++++----- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 5237ea18aee64e..a8928fbfbd7ebb 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -54,21 +54,21 @@ def eraseInstances (e : Expr) : MetaM Expr := do args := args.set! i default return mkAppN f args -/-- Compute the data carrying parent classes of `cls`. -This excludes parent classes that have a data carrying parent themselves. +/-- Compute the parent classes of `cls`, excluding parent classes that have a parent themselves. The reason to exclude such classes is that if there is a duplication in such a class, -then there will necessarily also be a duplication in all of its parents. -If `cls` is a `Prop` or a non-structure class, this simply returns `#[cls]`. +then there will necessarily also be a duplication in its parent. +If `cls` carries data, then only consider parents that carry data. +If `cls` is a non-structure class, this simply returns `#[cls]`. The resulting expressions contain bound variables that correspond to the parameters of `cls`. The universe levels and bound variables need to be instantiated to get concrete data projections. -/ -partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do +partial def getAbstractProjections (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls - MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do + MetaM.run' <| forallTelescope cinfo.type fun xs type ↦ do withLocalDeclD `self (mkAppN (.const cls (cinfo.levelParams.map .param)) xs) fun inst ↦ do - go cls inst #[] xs |>.run' {} + go cls inst #[] xs type.isProp |>.run' {} where - go (cls : Name) (inst : Expr) (acc : Array Expr) (xs : Array Expr) : + go (cls : Name) (inst : Expr) (acc : Array Expr) (xs : Array Expr) (isProp : Bool) : StateRefT NameSet MetaM (Array Expr) := do let type ← whnf (← inferType inst) let mut acc := acc @@ -79,9 +79,10 @@ where let parent := info.structName if (← get).contains parent then continue modify (·.insert parent) - if (← getConstInfo parent).type.getForallBody.isProp then continue + unless isProp do + if (← getConstInfo parent).type.getForallBody.isProp then continue let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst - acc ← go parent proj acc xs + acc ← go parent proj acc xs isProp anyParent := true if !anyParent then acc := acc.push (← eraseInstances (type.abstract xs)) @@ -92,15 +93,15 @@ initialize dataProjectionCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} /-- Return the result of `getAbstractDataProjections`, using a global cache. To ensure soundness, the cache is only used for imported declarations. -/ -def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do +def getAbstractProjectionsCached (cls : Name) : CoreM (Array Expr) := do if (← getEnv).isImportedConst cls then if let some result := (← dataProjectionCache.get).find? cls then return result - let result ← getAbstractDataProjections cls + let result ← getAbstractProjections cls dataProjectionCache.modify (·.insert cls result) return result else - getAbstractDataProjections cls + getAbstractProjections cls /-- Find classes for which multiple different instances can be synthesized in the local context. The result maps classes to the (at least 2) local instances that generate them. -/ @@ -118,7 +119,7 @@ partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do let .const cls us := f | return #[] -- This can happen when using `set_option checkBinderAnnotations false` let levelParams := (← getConstInfo cls).levelParams - let projs ← getAbstractDataProjectionsCached cls + let projs ← getAbstractProjectionsCached cls projs.mapM fun proj ↦ mkForallFVars xs <| (proj.instantiateLevelParams levelParams us).instantiateRev args for projCls in projClasses do @@ -155,7 +156,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option else let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") - msgs := msgs.push <| m!"{fvarTypes} give conflicting instances of {overlaps}." + msgs := msgs.push <| m!"{fvarTypes} give different instances of {overlaps}." let declDescr ← if let some decl := ctx.parentDecl? then -- Use `addMessageContextPartial` to clear the local context, diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 47b2124b089eca..286f10322219c0 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -47,7 +47,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @ +3:21...+4:12 warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -63,7 +63,7 @@ set_option linter.overlappingInstances true in warning: Declaration `foo₂` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -88,7 +88,7 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true warning: Declaration `foo₄` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give different instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -100,7 +100,7 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- warning: Declaration `foo₅` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]` and `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]` and `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -112,8 +112,8 @@ lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial /-- warning: Declaration `foo₆` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]`. -• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -227,7 +227,7 @@ class B (α : Type u) extends A α /-- warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` give conflicting instances of `[A α]`. +`[B α]` and `[A α]` give different instances of `[A α]`. Consider choosing different instance hypotheses. @@ -253,7 +253,7 @@ class B' (α β : Type*) [A' α] extends B α β where /-- warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` give conflicting instances of `[B α β]`. +`[B α β]` and `[B' α β]` give different instances of `[B α β]`. Consider choosing different instance hypotheses. From 99d291a2b5e34b6aeebca11257d66756f82e2ef5 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 30 Apr 2026 16:20:22 +0100 Subject: [PATCH 107/141] fix for a class extending a non-class --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 1 + MathlibTest/OverlappingInstances.lean | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a8928fbfbd7ebb..24e42989210739 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -79,6 +79,7 @@ where let parent := info.structName if (← get).contains parent then continue modify (·.insert parent) + unless ← isInstance info.projFn do continue unless isProp do if (← getConstInfo parent).type.getForallBody.isProp then continue let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 286f10322219c0..73515c064fc433 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -284,3 +284,11 @@ where -- Sadly, the linter does not work when the declaration doesn't have a body: class FooClass (α : Type) [Add α] [Add α] where + +/-! Test classes that extend a non-class -/ + +structure NotAClass where +class IsAClass1 extends NotAClass +class IsAClass2 extends NotAClass + +example [IsAClass1] [IsAClass2] : True := trivial From 62d4fa1342298f367ff0ae4454220e5f314b2198 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 30 Apr 2026 18:40:02 +0100 Subject: [PATCH 108/141] Revert "attempt 2 for linting against Prop classes" This reverts commit 4e2b7a2a725732cbb77f27c0e4fabe70ea710d24. --- .../Tactic/Linter/OverlappingInstances.lean | 31 +++++++++---------- MathlibTest/OverlappingInstances.lean | 16 +++++----- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 24e42989210739..51a9f7e1b7453e 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -54,21 +54,21 @@ def eraseInstances (e : Expr) : MetaM Expr := do args := args.set! i default return mkAppN f args -/-- Compute the parent classes of `cls`, excluding parent classes that have a parent themselves. +/-- Compute the data carrying parent classes of `cls`. +This excludes parent classes that have a data carrying parent themselves. The reason to exclude such classes is that if there is a duplication in such a class, -then there will necessarily also be a duplication in its parent. -If `cls` carries data, then only consider parents that carry data. -If `cls` is a non-structure class, this simply returns `#[cls]`. +then there will necessarily also be a duplication in all of its parents. +If `cls` is a `Prop` or a non-structure class, this simply returns `#[cls]`. The resulting expressions contain bound variables that correspond to the parameters of `cls`. The universe levels and bound variables need to be instantiated to get concrete data projections. -/ -partial def getAbstractProjections (cls : Name) : CoreM (Array Expr) := do +partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls - MetaM.run' <| forallTelescope cinfo.type fun xs type ↦ do + MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do withLocalDeclD `self (mkAppN (.const cls (cinfo.levelParams.map .param)) xs) fun inst ↦ do - go cls inst #[] xs type.isProp |>.run' {} + go cls inst #[] xs |>.run' {} where - go (cls : Name) (inst : Expr) (acc : Array Expr) (xs : Array Expr) (isProp : Bool) : + go (cls : Name) (inst : Expr) (acc : Array Expr) (xs : Array Expr) : StateRefT NameSet MetaM (Array Expr) := do let type ← whnf (← inferType inst) let mut acc := acc @@ -80,10 +80,9 @@ where if (← get).contains parent then continue modify (·.insert parent) unless ← isInstance info.projFn do continue - unless isProp do - if (← getConstInfo parent).type.getForallBody.isProp then continue + if (← getConstInfo parent).type.getForallBody.isProp then continue let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst - acc ← go parent proj acc xs isProp + acc ← go parent proj acc xs anyParent := true if !anyParent then acc := acc.push (← eraseInstances (type.abstract xs)) @@ -94,15 +93,15 @@ initialize dataProjectionCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} /-- Return the result of `getAbstractDataProjections`, using a global cache. To ensure soundness, the cache is only used for imported declarations. -/ -def getAbstractProjectionsCached (cls : Name) : CoreM (Array Expr) := do +def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do if (← getEnv).isImportedConst cls then if let some result := (← dataProjectionCache.get).find? cls then return result - let result ← getAbstractProjections cls + let result ← getAbstractDataProjections cls dataProjectionCache.modify (·.insert cls result) return result else - getAbstractProjections cls + getAbstractDataProjections cls /-- Find classes for which multiple different instances can be synthesized in the local context. The result maps classes to the (at least 2) local instances that generate them. -/ @@ -120,7 +119,7 @@ partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do let .const cls us := f | return #[] -- This can happen when using `set_option checkBinderAnnotations false` let levelParams := (← getConstInfo cls).levelParams - let projs ← getAbstractProjectionsCached cls + let projs ← getAbstractDataProjectionsCached cls projs.mapM fun proj ↦ mkForallFVars xs <| (proj.instantiateLevelParams levelParams us).instantiateRev args for projCls in projClasses do @@ -157,7 +156,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option else let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") - msgs := msgs.push <| m!"{fvarTypes} give different instances of {overlaps}." + msgs := msgs.push <| m!"{fvarTypes} give conflicting instances of {overlaps}." let declDescr ← if let some decl := ctx.parentDecl? then -- Use `addMessageContextPartial` to clear the local context, diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 73515c064fc433..ae555373990480 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -47,7 +47,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @ +3:21...+4:12 warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -63,7 +63,7 @@ set_option linter.overlappingInstances true in warning: Declaration `foo₂` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -88,7 +88,7 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true warning: Declaration `foo₄` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give different instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -100,7 +100,7 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- warning: Declaration `foo₅` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]` and `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]` and `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -112,8 +112,8 @@ lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial /-- warning: Declaration `foo₆` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]`. -• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -227,7 +227,7 @@ class B (α : Type u) extends A α /-- warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` give different instances of `[A α]`. +`[B α]` and `[A α]` give conflicting instances of `[A α]`. Consider choosing different instance hypotheses. @@ -253,7 +253,7 @@ class B' (α β : Type*) [A' α] extends B α β where /-- warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` give different instances of `[B α β]`. +`[B α β]` and `[B' α β]` give conflicting instances of `[B α β]`. Consider choosing different instance hypotheses. From 818f6ca66f85feb6fa76ebf63a3cee974c5d4245 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 1 May 2026 06:46:04 +0100 Subject: [PATCH 109/141] Attempt no. 3 for linting against Prop classes --- .../Tactic/Linter/OverlappingInstances.lean | 77 ++++++++++++------- MathlibTest/OverlappingInstances.lean | 50 ++++++++++-- 2 files changed, 90 insertions(+), 37 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 51a9f7e1b7453e..be0193a1faed9b 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -12,17 +12,18 @@ public meta import Batteries.Lean.Position public meta import Mathlib.Tactic.Linter.UnusedInstancesInType /-! -# A linter for declarations with local instances that have overlapping data +# A linter for declarations with local instances that overlap -If the same data can be obtained from two different instances in the local context, we risk having -non-defeq versions of that data. This situation, both for declarations and more broadly, is known -as an "instance diamond". This linter warns against instance diamonds in local contexts. +If the same data can be obtained from two different instances, we risk having +non-defeq versions of that data. This situation is known as an "instance diamond". +This linter warns against instance diamonds in local contexts. This is a syntax linter. It is run on partially and fully elaborated declarations. -To find diamonds, we compute all data carrying parent classes of any given class. -For classes that are propositions or aren't structures, this returns the class itself. +To find diamonds, we compute all parent classes of any given local instance. +For classes that aren't structures, this returns the class itself. If any of these classes is duplicated, we throw a warning. +This also searches for redundant proposition classes. A common case where this linter may fire is if the same type class assumption is given in both a `variable` statement and a declaration. This kind of variable shadowing does not actually produce @@ -54,21 +55,21 @@ def eraseInstances (e : Expr) : MetaM Expr := do args := args.set! i default return mkAppN f args -/-- Compute the data carrying parent classes of `cls`. -This excludes parent classes that have a data carrying parent themselves. +/-- Compute the parent classes of `cls`, excluding parent classes that have a parent themselves. The reason to exclude such classes is that if there is a duplication in such a class, -then there will necessarily also be a duplication in all of its parents. -If `cls` is a `Prop` or a non-structure class, this simply returns `#[cls]`. +then there will necessarily also be a duplication in its parent. +If `cls` carries data, then only consider parents that carry data. +If `cls` is a non-structure class, this simply returns `#[cls]`. The resulting expressions contain bound variables that correspond to the parameters of `cls`. The universe levels and bound variables need to be instantiated to get concrete data projections. -/ -partial def getAbstractDataProjections (cls : Name) : CoreM (Array Expr) := do +partial def getAbstractProjections (cls : Name) : CoreM (Array Expr) := do let cinfo ← getConstInfo cls - MetaM.run' <| forallTelescope cinfo.type fun xs _ ↦ do + MetaM.run' <| forallTelescope cinfo.type fun xs type ↦ do withLocalDeclD `self (mkAppN (.const cls (cinfo.levelParams.map .param)) xs) fun inst ↦ do - go cls inst #[] xs |>.run' {} + go cls inst #[] xs type.isProp |>.run' {} where - go (cls : Name) (inst : Expr) (acc : Array Expr) (xs : Array Expr) : + go (cls : Name) (inst : Expr) (acc : Array Expr) (xs : Array Expr) (isProp : Bool) : StateRefT NameSet MetaM (Array Expr) := do let type ← whnf (← inferType inst) let mut acc := acc @@ -80,32 +81,33 @@ where if (← get).contains parent then continue modify (·.insert parent) unless ← isInstance info.projFn do continue - if (← getConstInfo parent).type.getForallBody.isProp then continue + unless isProp do + if (← getConstInfo parent).type.getForallBody.isProp then continue let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst - acc ← go parent proj acc xs + acc ← go parent proj acc xs isProp anyParent := true if !anyParent then acc := acc.push (← eraseInstances (type.abstract xs)) return acc /-- A cache for the result of `getAbstractDataProjections`. -/ -initialize dataProjectionCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} +initialize classProjectionsCache : IO.Ref (NameMap (Array Expr)) ← IO.mkRef {} /-- Return the result of `getAbstractDataProjections`, using a global cache. To ensure soundness, the cache is only used for imported declarations. -/ -def getAbstractDataProjectionsCached (cls : Name) : CoreM (Array Expr) := do +def getAbstractProjectionsCached (cls : Name) : CoreM (Array Expr) := do if (← getEnv).isImportedConst cls then - if let some result := (← dataProjectionCache.get).find? cls then + if let some result := (← classProjectionsCache.get).find? cls then return result - let result ← getAbstractDataProjections cls - dataProjectionCache.modify (·.insert cls result) + let result ← getAbstractProjections cls + classProjectionsCache.modify (·.insert cls result) return result else - getAbstractDataProjections cls + getAbstractProjections cls /-- Find classes for which multiple different instances can be synthesized in the local context. The result maps classes to the (at least 2) local instances that generate them. -/ -partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do +partial def findOverlappingInstances : MetaM (ExprMap (Array FVarId)) := do -- Maps a class to the collection of local instances that overlap on it. -- This only includes overlaps of at least 2 local instances. let mut overlaps : ExprMap (Array FVarId) := {} @@ -119,7 +121,7 @@ partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do let .const cls us := f | return #[] -- This can happen when using `set_option checkBinderAnnotations false` let levelParams := (← getConstInfo cls).levelParams - let projs ← getAbstractDataProjectionsCached cls + let projs ← getAbstractProjectionsCached cls projs.mapM fun proj ↦ mkForallFVars xs <| (proj.instantiateLevelParams levelParams us).instantiateRev args for projCls in projClasses do @@ -129,19 +131,20 @@ partial def findOverlappingDataInstances : MetaM (ExprMap (Array FVarId)) := do encountered := encountered.insert projCls decl.fvarId return overlaps -/-- Lints against data-carrying overlaps between instances in the local contexts of declarations. -/ +/-- Lints against overlaps between instances in the local contexts of declarations. -/ register_option linter.overlappingInstances : Bool := { defValue := true descr := "enable the overlapping instances linter." } -/-- Report a warning message if there are any overlapping instances in the local context. -/ +/-- Report a warning message if there are any overlapping instances in the local context. +For `Prop` instances, only report local instances that are redundant. -/ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option Expr) : IO (Option MessageData) := do ctx.runMetaM lctx do -- Add the hypotheses of the expected type to the local context, as it may have more instances. expectedType?.elim id (forallTelescope · fun _ _ => ·) do - let overlaps ← findOverlappingDataInstances + let overlaps ← findOverlappingInstances if overlaps.isEmpty then return none let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := @@ -154,9 +157,25 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option if fvarTypes.all (· == fvarTypes[0]!) then msgs := msgs.push <| m!"There are {fvarTypes.size} `{.sbracket fvarTypes[0]!}` instances." else + let localInsts := (← getLCtx).decls.toList.reduceOption + let mut redundant := #[] + for fvar in fvars, type in fvarTypes do + let localInsts := localInsts.filter (·.fvarId != fvar) + if (← withLocalInstances localInsts (trySynthInstance type)) matches .some _ then + redundant := redundant.push type + if redundant.isEmpty then + if ← isProp fvarTypes[0]! then + continue let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") - msgs := msgs.push <| m!"{fvarTypes} give conflicting instances of {overlaps}." + let mut msg := m!"{fvarTypes} give different instances of {overlaps}." + unless redundant.isEmpty do + let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") + msg := + m!"{msg}\nOut of these, {redundant'} {ite (redundant.size = 1) "is" "are"} redundant." + msgs := msgs.push msg + if msgs.isEmpty then + return none let declDescr ← if let some decl := ctx.parentDecl? then -- Use `addMessageContextPartial` to clear the local context, @@ -167,7 +186,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let mut msg := m!"{declDescr} has overlapping instances:" -- Create a bulleted list if there are multiple messages, otherwise just a single line msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else - msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {·}") + msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {.nestD ·}") msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." addMessageContextFull msg diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index ae555373990480..1c19457fe787e2 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -47,7 +47,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @ +3:21...+4:12 warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -63,7 +63,8 @@ set_option linter.overlappingInstances true in warning: Declaration `foo₂` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. + Out of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` are redundant. Consider choosing different instance hypotheses. @@ -88,7 +89,8 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true warning: Declaration `foo₄` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give different instances of `[SubBar Nat]`. + Out of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` are redundant. Consider choosing different instance hypotheses. @@ -100,7 +102,7 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- warning: Declaration `foo₅` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]` and `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]` and `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -112,8 +114,8 @@ lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial /-- warning: Declaration `foo₆` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]`. -• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -140,6 +142,36 @@ private def foo [Add Nat] [Add Nat] : Bool := true end Foo +section prop + +class IsFoo : Prop + +class IsBar : Prop extends IsFoo + +class IsBaz : Prop extends IsBar + +/-- +warning: Declaration `_example` has overlapping instances: + +`[IsBar]` and `[IsBaz]` give different instances of `[IsFoo]`. +Out of these, `[IsBar]` is redundant. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example [IsBar] [IsBaz] : True := trivial + +/-! Don't warn when data overlaps on `Prop` -/ + +class IsBarPlus : Type extends IsBar +class IsBazPlus : Type extends IsBaz + +example [IsBarPlus] [IsBazPlus] : True := trivial + +end prop + section duplicates /-! Make sure we warn on duplicate inductive classes and duplicate `Prop` classes. -/ @@ -227,7 +259,8 @@ class B (α : Type u) extends A α /-- warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` give conflicting instances of `[A α]`. +`[B α]` and `[A α]` give different instances of `[A α]`. +Out of these, `[A α]` is redundant. Consider choosing different instance hypotheses. @@ -253,7 +286,8 @@ class B' (α β : Type*) [A' α] extends B α β where /-- warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` give conflicting instances of `[B α β]`. +`[B α β]` and `[B' α β]` give different instances of `[B α β]`. +Out of these, `[B α β]` is redundant. Consider choosing different instance hypotheses. From af29da49eafe6d6e0ce343ed918c59764aba9832 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 1 May 2026 13:41:38 +0100 Subject: [PATCH 110/141] new adaptations --- Mathlib/Algebra/Category/Ring/FilteredColimits.lean | 1 + Mathlib/Algebra/Module/DedekindDomain.lean | 4 +--- Mathlib/Algebra/Order/Invertible.lean | 3 +-- Mathlib/Algebra/Polynomial/Div.lean | 2 +- Mathlib/Algebra/Star/StarRingHom.lean | 13 ++++++------- Mathlib/Analysis/Convex/StdSimplex.lean | 2 ++ Mathlib/Analysis/Normed/Field/Krasner.lean | 4 ++-- .../ContinuousFunctionalCalculus/Rpow/ConjSqrt.lean | 2 ++ Mathlib/Analysis/SpecificLimits/Normed.lean | 1 + .../MorphismProperty/OverAdjunction.lean | 2 ++ Mathlib/FieldTheory/Normal/Defs.lean | 3 ++- Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean | 4 ++-- Mathlib/LinearAlgebra/Eigenspace/Matrix.lean | 2 ++ Mathlib/LinearAlgebra/RootSystem/Finite/G2.lean | 1 + .../RootSystem/GeckConstruction/Lemmas.lean | 1 + Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean | 2 +- Mathlib/MeasureTheory/Measure/Haar/Unique.lean | 3 +-- Mathlib/NumberTheory/ClassNumber/Finite.lean | 2 ++ .../NumberTheory/RamificationInertia/Galois.lean | 2 ++ Mathlib/RingTheory/ChainOfDivisors.lean | 4 ++++ Mathlib/RingTheory/ClassGroup.lean | 2 ++ Mathlib/RingTheory/DedekindDomain/Different.lean | 4 ++++ Mathlib/RingTheory/DedekindDomain/Dvr.lean | 2 ++ Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean | 1 + Mathlib/RingTheory/DedekindDomain/Instances.lean | 2 ++ .../RingTheory/DedekindDomain/IntegralClosure.lean | 2 ++ .../RingTheory/DedekindDomain/LinearDisjoint.lean | 2 ++ Mathlib/RingTheory/DiscreteValuationRing/TFAE.lean | 4 ++-- Mathlib/RingTheory/FractionalIdeal/Extended.lean | 2 ++ Mathlib/RingTheory/HahnSeries/Lex.lean | 2 +- Mathlib/RingTheory/Ideal/Norm/AbsNorm.lean | 4 ++-- Mathlib/RingTheory/Ideal/Norm/RelNorm.lean | 2 ++ Mathlib/RingTheory/NormalClosure.lean | 2 ++ Mathlib/Tactic/NormNum/Ineq.lean | 8 ++++---- .../Topology/Algebra/Nonarchimedean/Completion.lean | 3 +-- 35 files changed, 68 insertions(+), 32 deletions(-) diff --git a/Mathlib/Algebra/Category/Ring/FilteredColimits.lean b/Mathlib/Algebra/Category/Ring/FilteredColimits.lean index 6d16674327f61a..5c8c249872fc8f 100644 --- a/Mathlib/Algebra/Category/Ring/FilteredColimits.lean +++ b/Mathlib/Algebra/Category/Ring/FilteredColimits.lean @@ -376,6 +376,7 @@ protected lemma nontrivial {F : J ⥤ CommRingCat.{v}} [IsFilteredOrEmpty J] (Types.FilteredColimit.isColimit_eq_iff' (isColimitOfPreserves (forget _) hc) _ _).mp h exact zero_ne_one (((F.map f).hom.map_zero.symm.trans e).trans (F.map f).hom.map_one) +set_option linter.overlappingInstances false in omit [IsFiltered J] in instance {F : J ⥤ CommRingCat.{v}} [IsFilteredOrEmpty J] [HasColimit F] [∀ i, Nontrivial (F.obj i)] : Nontrivial ↑(Limits.colimit F) := diff --git a/Mathlib/Algebra/Module/DedekindDomain.lean b/Mathlib/Algebra/Module/DedekindDomain.lean index 6d4ed4a7c864f9..c918549ed0cd2f 100644 --- a/Mathlib/Algebra/Module/DedekindDomain.lean +++ b/Mathlib/Algebra/Module/DedekindDomain.lean @@ -22,14 +22,12 @@ public section universe u v -variable {R : Type u} [CommRing R] [IsDomain R] {M : Type v} [AddCommGroup M] [Module R M] +variable {R : Type u} [CommRing R] [IsDedekindDomain R] {M : Type v} [AddCommGroup M] [Module R M] open scoped DirectSum namespace Submodule -variable [IsDedekindDomain R] - open UniqueFactorizationMonoid /-- Over a Dedekind domain, an `I`-torsion module is the internal direct sum of its `p i ^ e i`- diff --git a/Mathlib/Algebra/Order/Invertible.lean b/Mathlib/Algebra/Order/Invertible.lean index ccdf4d4e138f23..983786f2539f8e 100644 --- a/Mathlib/Algebra/Order/Invertible.lean +++ b/Mathlib/Algebra/Order/Invertible.lean @@ -41,6 +41,5 @@ theorem invOf_le_one [Invertible a] (h : 1 ≤ a) : ⅟a ≤ 1 := theorem invOf_lt_one [Invertible a] (h : 1 < a) : ⅟a < 1 := mul_invOf_self a ▸ lt_mul_of_one_lt_left (invOf_pos.2 <| one_pos.trans h) h -theorem pos_invOf_of_invertible_cast [Nontrivial R] (n : ℕ) - [Invertible (n : R)] : 0 < ⅟(n : R) := +theorem pos_invOf_of_invertible_cast (n : ℕ) [Invertible (n : R)] : 0 < ⅟(n : R) := invOf_pos.2 <| Nat.cast_pos.2 <| pos_of_invertible_cast (R := R) n diff --git a/Mathlib/Algebra/Polynomial/Div.lean b/Mathlib/Algebra/Polynomial/Div.lean index 29a02efd5c24d1..8bd6a5aafd3e33 100644 --- a/Mathlib/Algebra/Polynomial/Div.lean +++ b/Mathlib/Algebra/Polynomial/Div.lean @@ -789,7 +789,7 @@ lemma _root_.Irreducible.isRoot_eq_bot_of_natDegree_ne_one (hi : Irreducible p) (hdeg : p.natDegree ≠ 1) : p.IsRoot = ⊥ := le_bot_iff.mp fun _ ↦ hi.not_isRoot_of_natDegree_ne_one hdeg -lemma _root_.Irreducible.subsingleton_isRoot [IsLeftCancelMulZero R] +lemma _root_.Irreducible.subsingleton_isRoot (hi : Irreducible p) : { x | p.IsRoot x }.Subsingleton := fun _ hx ↦ (subsingleton_isRoot_of_natDegree_eq_one <| natDegree_eq_of_degree_eq_some <| degree_eq_one_of_irreducible_of_root hi hx) hx diff --git a/Mathlib/Algebra/Star/StarRingHom.lean b/Mathlib/Algebra/Star/StarRingHom.lean index b0e2bbd2e5bf0a..152ccec5e3329d 100644 --- a/Mathlib/Algebra/Star/StarRingHom.lean +++ b/Mathlib/Algebra/Star/StarRingHom.lean @@ -260,27 +260,26 @@ namespace StarRingEquivClass -- See note [lower instance priority] instance (priority := 50) {F A B : Type*} [Add A] [Mul A] [Star A] [Add B] [Mul B] [Star B] [EquivLike F A B] [hF : StarRingEquivClass F A B] : - StarHomClass F A B := - { hF with } + StarHomClass F A B where + __ := hF -- See note [lower instance priority] instance (priority := 100) {F A B : Type*} [NonUnitalNonAssocSemiring A] [Star A] - [NonUnitalNonAssocSemiring B] [Star B] [EquivLike F A B] [RingEquivClass F A B] - [StarRingEquivClass F A B] : NonUnitalStarRingHomClass F A B := - { } + [NonUnitalNonAssocSemiring B] [Star B] [EquivLike F A B] [StarRingEquivClass F A B] : + NonUnitalStarRingHomClass F A B where /-- Turn an element of a type `F` satisfying `StarRingEquivClass F A B` into an actual `StarRingEquiv`. This is declared as the default coercion from `F` to `A ≃⋆+* B`. -/ @[coe] def toStarRingEquiv {F A B : Type*} [Add A] [Mul A] [Star A] [Add B] [Mul B] [Star B] - [EquivLike F A B] [RingEquivClass F A B] [StarRingEquivClass F A B] (f : F) : A ≃⋆+* B := + [EquivLike F A B] [StarRingEquivClass F A B] (f : F) : A ≃⋆+* B := { (RingEquivClass.toRingEquiv f : A ≃+* B) with map_star' := map_star f } /-- Any type satisfying `StarRingEquivClass` can be cast into `StarRingEquiv` via `StarRingEquivClass.toStarRingEquiv`. -/ instance instCoeHead {F A B : Type*} [Add A] [Mul A] [Star A] [Add B] [Mul B] [Star B] - [EquivLike F A B] [RingEquivClass F A B] [StarRingEquivClass F A B] : CoeHead F (A ≃⋆+* B) := + [EquivLike F A B] [StarRingEquivClass F A B] : CoeHead F (A ≃⋆+* B) := ⟨toStarRingEquiv⟩ end StarRingEquivClass diff --git a/Mathlib/Analysis/Convex/StdSimplex.lean b/Mathlib/Analysis/Convex/StdSimplex.lean index 795c64bf9773fc..66a5417565b966 100644 --- a/Mathlib/Analysis/Convex/StdSimplex.lean +++ b/Mathlib/Analysis/Convex/StdSimplex.lean @@ -86,6 +86,8 @@ theorem ite_eq_mem_stdSimplex (i : ι) : (if i = · then (1 : 𝕜) else 0) ∈ variable [IsOrderedRing 𝕜] +set_option linter.overlappingInstances false + #adaptation_note /-- nightly-2024-03-11 we need a type annotation on the segment in the following two lemmas. -/ diff --git a/Mathlib/Analysis/Normed/Field/Krasner.lean b/Mathlib/Analysis/Normed/Field/Krasner.lean index 8966dac2e328b4..2054044e2888d8 100644 --- a/Mathlib/Analysis/Normed/Field/Krasner.lean +++ b/Mathlib/Analysis/Normed/Field/Krasner.lean @@ -67,7 +67,7 @@ theorem krasner [Field K] [Algebra K L] IsKrasner.krasner' hx sp hy h variable [NontriviallyNormedField K] [CompleteSpace K] [IsUltrametricDist K] - [NormedAlgebra K L] [Algebra.IsAlgebraic K L] + [NormedAlgebra K L] /-- Krasner's lemma assuming `Normal K L`. -/ theorem of_completeSpace_of_normal [Normal K L] : IsKrasner K L where @@ -114,7 +114,7 @@ If `K` is a complete nontrivially normed field and `L` is an algebraic extension such that the norm of `L` extends the norm on `K`, then `IsKrasner K L` holds. This corresponds to the classical Krasner's lemma. -/ -instance of_completeSpace : IsKrasner K L where +instance of_completeSpace [Algebra.IsAlgebraic K L] : IsKrasner K L where krasner' {x} {y} xsep sp yint kr := by -- Reduce to the case `L = algebraic closure of K` to apply the previous lemma. let C := AlgebraicClosure K diff --git a/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Rpow/ConjSqrt.lean b/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Rpow/ConjSqrt.lean index 4d1f3e99375b2b..9830ed8aa37e5e 100644 --- a/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Rpow/ConjSqrt.lean +++ b/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/Rpow/ConjSqrt.lean @@ -55,6 +55,8 @@ lemma conjSqrt_le_conjSqrt {c a b : A} (h : a ≤ b) : conjSqrt c a ≤ conjSqrt variable [IsSemitopologicalRing A] [T2Space A] +set_option linter.overlappingInstances false + @[grind =] lemma isStrictlyPositive_conjSqrt_iff (c a : A) (hc : IsStrictlyPositive c := by cfc_tac) : IsStrictlyPositive (conjSqrt c a) ↔ IsStrictlyPositive a := by diff --git a/Mathlib/Analysis/SpecificLimits/Normed.lean b/Mathlib/Analysis/SpecificLimits/Normed.lean index 92006656fc789c..350d91d01129df 100644 --- a/Mathlib/Analysis/SpecificLimits/Normed.lean +++ b/Mathlib/Analysis/SpecificLimits/Normed.lean @@ -950,6 +950,7 @@ lemma tendsto_smul_congr_of_tendsto_left_cobounded_of_isBoundedUnder · rw [← tendsto_zero_iff_norm_tendsto_zero] exact tendsto_zero_of_isBoundedUnder_smul_of_tendsto_cobounded hmul.norm.isBoundedUnder_le hf₁ +set_option linter.overlappingInstances false in -- The use case in mind for this is when `K = ℝ`, and `R = ℝ` or `ℂ` lemma tendsto_smul_comp_nat_floor_of_tendsto_nsmul [NormSMulClass ℤ K] [LinearOrder K] [IsStrictOrderedRing K] [FloorSemiring K] [HasSolidNorm K] {g : ℕ → R} {t : R} diff --git a/Mathlib/CategoryTheory/MorphismProperty/OverAdjunction.lean b/Mathlib/CategoryTheory/MorphismProperty/OverAdjunction.lean index 5321ab771230cc..2c07b52274074d 100644 --- a/Mathlib/CategoryTheory/MorphismProperty/OverAdjunction.lean +++ b/Mathlib/CategoryTheory/MorphismProperty/OverAdjunction.lean @@ -56,6 +56,7 @@ def Over.mapCongr [Q.RespectsIso] {X Y : T} {f g : X ⟶ Y} (hfg : f = g) (hf : Over.map Q hf ≅ Over.map (f := g) Q (by cat_disch) := NatIso.ofComponents (fun Y ↦ Over.isoMk (Iso.refl _)) +set_option linter.overlappingInstances false in /-- `Over.map` preserves identities. -/ @[simps!] def Over.mapId [P.IsMultiplicative] [Q.RespectsIso] (X : T) (f : X ⟶ X := 𝟙 X) @@ -216,6 +217,7 @@ def Under.mapCongr [Q.RespectsIso] {X Y : T} {f g : X ⟶ Y} (hfg : f = g) (hf : Under.map Q hf ≅ Under.map (f := g) Q (by cat_disch) := NatIso.ofComponents (fun Y ↦ Under.isoMk (Iso.refl _)) +set_option linter.overlappingInstances false in /-- `Under.map` preserves identities. -/ @[simps!] def Under.mapId [P.IsMultiplicative] [Q.RespectsIso] (X : T) (f : X ⟶ X := 𝟙 X) diff --git a/Mathlib/FieldTheory/Normal/Defs.lean b/Mathlib/FieldTheory/Normal/Defs.lean index 85698b4588b1cb..f10139cb6668d6 100644 --- a/Mathlib/FieldTheory/Normal/Defs.lean +++ b/Mathlib/FieldTheory/Normal/Defs.lean @@ -90,9 +90,10 @@ theorem AlgEquiv.transfer_normal (f : E ≃ₐ[F] E') : Normal F E ↔ Normal F ⟨fun _ ↦ Normal.of_algEquiv f, fun _ ↦ Normal.of_algEquiv f.symm⟩ theorem Normal.of_equiv_equiv {M N : Type*} [Field N] [Field M] [Algebra M N] - [Algebra.IsAlgebraic F E] [h : Normal F E] {f : F ≃+* M} {g : E ≃+* N} + [h : Normal F E] {f : F ≃+* M} {g : E ≃+* N} (hcomp : (algebraMap M N).comp f = (g : E →+* N).comp (algebraMap F E)) : Normal M N := by + have := h rw [normal_iff] at h ⊢ intro x rw [← g.apply_symm_apply x] diff --git a/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean b/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean index 0f42be79c7aaf4..565192aeeecea4 100644 --- a/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean +++ b/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean @@ -200,8 +200,8 @@ theorem iIsOrtho.not_isOrtho_basis_self_of_nondegenerate {n : Type w} [Nontrivia /-- Given an orthogonal basis with respect to a bilinear form, the bilinear form is nondegenerate iff the basis has no elements which are self-orthogonal. -/ -theorem iIsOrtho.nondegenerate_iff_not_isOrtho_basis_self {n : Type w} [Nontrivial R] - [IsDomain R] (B : BilinForm R M) (v : Basis n R M) (hO : B.iIsOrtho v) : +theorem iIsOrtho.nondegenerate_iff_not_isOrtho_basis_self {n : Type w} [IsDomain R] + (B : BilinForm R M) (v : Basis n R M) (hO : B.iIsOrtho v) : B.Nondegenerate ↔ ∀ i, ¬B.IsOrtho (v i) (v i) := ⟨hO.not_isOrtho_basis_self_of_nondegenerate, hO.nondegenerate_of_not_isOrtho_basis_self _⟩ diff --git a/Mathlib/LinearAlgebra/Eigenspace/Matrix.lean b/Mathlib/LinearAlgebra/Eigenspace/Matrix.lean index 4fe412fc1d32c1..595b618e461c91 100644 --- a/Mathlib/LinearAlgebra/Eigenspace/Matrix.lean +++ b/Mathlib/LinearAlgebra/Eigenspace/Matrix.lean @@ -41,6 +41,8 @@ lemma hasEigenvector_toLin'_diagonal (d : n → R) (i : n) : HasEigenvector (toLin' (diagonal d)) (d i) (Pi.basisFun R n i) := hasEigenvector_toLin_diagonal _ _ (Pi.basisFun R n) +set_option linter.overlappingInstances false + /-- Eigenvalues of a diagonal linear operator are the diagonal entries. -/ lemma hasEigenvalue_toLin_diagonal_iff (d : n → R) {μ : R} [IsDomain R] [IsTorsionFree R M] (b : Basis n R M) : HasEigenvalue (toLin b b (diagonal d)) μ ↔ ∃ i, d i = μ := by diff --git a/Mathlib/LinearAlgebra/RootSystem/Finite/G2.lean b/Mathlib/LinearAlgebra/RootSystem/Finite/G2.lean index c0811e35ec06e8..e557a7f8d6b6e1 100644 --- a/Mathlib/LinearAlgebra/RootSystem/Finite/G2.lean +++ b/Mathlib/LinearAlgebra/RootSystem/Finite/G2.lean @@ -110,6 +110,7 @@ lemma not_isG2_iff_isNotG2 : · specialize h i j lia +set_option linter.overlappingInstances false in lemma IsG2.pairingIn_mem_zero_one_three [P.IsG2] (i j : ι) (h : P.root i ≠ P.root j) (h' : P.root i ≠ -P.root j) : P.pairingIn ℤ i j ∈ ({-3, -1, 0, 1, 3} : Set ℤ) := by diff --git a/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Lemmas.lean b/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Lemmas.lean index 78dcfd684d021f..6d6579bfee7413 100644 --- a/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Lemmas.lean +++ b/Mathlib/LinearAlgebra/RootSystem/GeckConstruction/Lemmas.lean @@ -110,6 +110,7 @@ section chainBotCoeff_mul_chainTopCoeff variable {b : P.Base} {i j k l m : ι} +set_option linter.overlappingInstances false in private lemma chainBotCoeff_mul_chainTopCoeff.aux_0 [P.IsNotG2] (hik_mem : P.root k + P.root i ∈ range P.root) : P.pairingIn ℤ k i = 0 ∨ (P.pairingIn ℤ k i < 0 ∧ P.chainBotCoeff i k = 0) := by diff --git a/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean b/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean index 33fb104a875f34..0613530983216e 100644 --- a/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean +++ b/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean @@ -993,7 +993,7 @@ lemma nondegenerate_restrict_iff_disjoint_ker (hs : ∀ x, 0 ≤ B x x) (hB : B. have key : x ∈ W ⊓ LinearMap.ker B := ⟨hx, h⟩ simpa [hW.eq_bot] using key -variable [IsDomain R] [IsTorsionFree R M] +variable [IsTorsionFree R M] /-- Strict **Cauchy-Schwarz** is equivalent to linear independence for positive definite forms. -/ lemma apply_mul_apply_lt_iff_linearIndependent (hp : ∀ x, x ≠ 0 → 0 < B x x) (x y : M) : diff --git a/Mathlib/MeasureTheory/Measure/Haar/Unique.lean b/Mathlib/MeasureTheory/Measure/Haar/Unique.lean index 10afcab099b9c8..d9ebe3aa1948ac 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/Unique.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/Unique.lean @@ -885,8 +885,7 @@ Given two left-invariant measures which are finite on compacts and regular, they coincide up to a multiplicative constant. -/ @[to_additive isAddLeftInvariant_eq_smul_of_regular] lemma isMulLeftInvariant_eq_smul_of_regular [LocallyCompactSpace G] - (μ' μ : Measure G) [IsHaarMeasure μ] [IsFiniteMeasureOnCompacts μ'] [IsMulLeftInvariant μ'] - [Regular μ] [Regular μ'] : + (μ' μ : Measure G) [IsHaarMeasure μ] [IsMulLeftInvariant μ'] [Regular μ] [Regular μ'] : μ' = haarScalarFactor μ' μ • μ := by have A : ∀ U, IsOpen U → μ' U = (haarScalarFactor μ' μ • μ) U := by intro U hU diff --git a/Mathlib/NumberTheory/ClassNumber/Finite.lean b/Mathlib/NumberTheory/ClassNumber/Finite.lean index bcd6c61e51b97d..4359bd39b47a5d 100644 --- a/Mathlib/NumberTheory/ClassNumber/Finite.lean +++ b/Mathlib/NumberTheory/ClassNumber/Finite.lean @@ -265,6 +265,8 @@ theorem ne_bot_of_prod_finsetApprox_mem (J : Ideal S) (h : algebraMap _ _ (∏ m ∈ finsetApprox bS adm, m) ∈ J) : J ≠ ⊥ := (Submodule.ne_bot_iff _).mpr ⟨_, h, prod_finsetApprox_ne_zero _ _⟩ +set_option linter.overlappingInstances false + /-- Each class in the class group contains an ideal `J` such that `M := Π m ∈ finsetApprox` is in `J`. -/ theorem exists_mk0_eq_mk0 [IsDedekindDomain S] [Algebra.IsAlgebraic R S] (I : (Ideal S)⁰) : diff --git a/Mathlib/NumberTheory/RamificationInertia/Galois.lean b/Mathlib/NumberTheory/RamificationInertia/Galois.lean index 7c92eadba90a9b..5ec6338cf410d0 100644 --- a/Mathlib/NumberTheory/RamificationInertia/Galois.lean +++ b/Mathlib/NumberTheory/RamificationInertia/Galois.lean @@ -210,6 +210,8 @@ theorem inertiaDegIn_mul_inertiaDegIn [p.IsMaximal] [P.IsMaximal] : rw [inertiaDegIn_eq_inertiaDeg p P G, inertiaDegIn_eq_inertiaDeg p Q GAC, inertiaDegIn_eq_inertiaDeg P Q GBC, inertiaDeg_algebra_tower p P Q] +set_option linter.overlappingInstances false + variable {p} in include G GAC GBC in theorem ramificationIdxIn_mul_ramificationIdxIn [IsDedekindDomain B] [IsDedekindDomain C] diff --git a/Mathlib/RingTheory/ChainOfDivisors.lean b/Mathlib/RingTheory/ChainOfDivisors.lean index 5030ac217e7549..10b5b1933ef631 100644 --- a/Mathlib/RingTheory/ChainOfDivisors.lean +++ b/Mathlib/RingTheory/ChainOfDivisors.lean @@ -244,6 +244,8 @@ variable [UniqueFactorizationMonoid N] [UniqueFactorizationMonoid M] open DivisorChain +set_option linter.overlappingInstances false + theorem pow_image_of_prime_by_factor_orderIso_dvd {m p : Associates M} {n : Associates N} (hn : n ≠ 0) (hp : p ∈ normalizedFactors m) (d : Set.Iic m ≃o Set.Iic n) {s : ℕ} (hs' : p ^ s ≤ m) : @@ -376,6 +378,8 @@ def mkFactorOrderIsoOfFactorDvdEquiv [IsCancelMulZero N] variable [UniqueFactorizationMonoid M] [UniqueFactorizationMonoid N] +set_option linter.overlappingInstances false + theorem mem_normalizedFactors_factor_dvd_iso_of_mem_normalizedFactors {m p : M} {n : N} (hm : m ≠ 0) (hn : n ≠ 0) (hp : p ∈ normalizedFactors m) {d : { l : M // l ∣ m } ≃ { l : N // l ∣ n }} (hd : ∀ l l', (d l : N) ∣ d l' ↔ (l : M) ∣ (l' : M)) : diff --git a/Mathlib/RingTheory/ClassGroup.lean b/Mathlib/RingTheory/ClassGroup.lean index d39be3cd05f639..b8431f5b966368 100644 --- a/Mathlib/RingTheory/ClassGroup.lean +++ b/Mathlib/RingTheory/ClassGroup.lean @@ -220,6 +220,8 @@ theorem ClassGroup.mk_canonicalEquiv (K' : Type*) [Field K'] [Algebra R K'] [IsF ← Units.map_comp, ← RingEquiv.coe_monoidHom_trans, FractionalIdeal.canonicalEquiv_trans_canonicalEquiv] +set_option linter.overlappingInstances false + /-- Send a nonzero integral ideal to an invertible fractional ideal. -/ def FractionalIdeal.mk0 [IsDedekindDomain R] : (Ideal R)⁰ →* (FractionalIdeal R⁰ K)ˣ where diff --git a/Mathlib/RingTheory/DedekindDomain/Different.lean b/Mathlib/RingTheory/DedekindDomain/Different.lean index 8bb3112ae3c969..1bc22d24c8b330 100644 --- a/Mathlib/RingTheory/DedekindDomain/Different.lean +++ b/Mathlib/RingTheory/DedekindDomain/Different.lean @@ -560,6 +560,8 @@ lemma differentialIdeal_le_iff {I : Ideal B} (hI : I ≠ ⊥) : variable (A K B L) +set_option linter.overlappingInstances false + open FractionalIdeal in /-- Transitivity of the different ideal. -/ theorem differentIdeal_eq_differentIdeal_mul_differentIdeal (C : Type*) [IsDomain B] [CommRing C] @@ -683,6 +685,8 @@ variable (L) variable [IsFractionRing B L] [IsDedekindDomain A] [IsDedekindDomain B] [IsTorsionFree A B] [Module.Finite A B] +set_option linter.overlappingInstances false + include K L in lemma pow_sub_one_dvd_differentIdeal_aux {p : Ideal A} [p.IsMaximal] (P : Ideal B) {e : ℕ} (he : e ≠ 0) (hp : p ≠ ⊥) diff --git a/Mathlib/RingTheory/DedekindDomain/Dvr.lean b/Mathlib/RingTheory/DedekindDomain/Dvr.lean index 3fb8dc74ce7cea..0c95766d324032 100644 --- a/Mathlib/RingTheory/DedekindDomain/Dvr.lean +++ b/Mathlib/RingTheory/DedekindDomain/Dvr.lean @@ -80,6 +80,8 @@ theorem Ring.DimensionLEOne.localization {R : Type*} (Rₘ : Type*) [CommRing R] refine h.not_lt_lt ⊥ (Ideal.comap _ _) (Ideal.comap _ _) ⟨?_, hlt⟩ exact IsLocalization.bot_lt_comap_prime _ _ hM _ hp0⟩ +set_option linter.overlappingInstances false + /-- The localization of a Dedekind domain is a Dedekind domain. -/ theorem IsLocalization.isDedekindDomain [IsDedekindDomain A] {M : Submonoid A} (hM : M ≤ A⁰) (Aₘ : Type*) [CommRing Aₘ] [IsDomain Aₘ] [Algebra A Aₘ] [IsLocalization M Aₘ] : diff --git a/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean b/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean index 351c7bb96a9ea4..3a02c232c791da 100644 --- a/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean +++ b/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean @@ -1189,6 +1189,7 @@ alias _root_.mem_primesOverFinset_iff := mem_primesOverFinset_iff end IsDedekindDomain +set_option linter.overlappingInstances false in variable {R} (A) in theorem IsLocalRing.primesOverFinset_eq [IsLocalRing A] [IsDedekindDomain A] [Algebra R A] [FaithfulSMul R A] [Module.Finite R A] {p : Ideal R} [p.IsMaximal] (hp0 : p ≠ ⊥) : diff --git a/Mathlib/RingTheory/DedekindDomain/Instances.lean b/Mathlib/RingTheory/DedekindDomain/Instances.lean index 86f51ed0f6f8fe..982a7cf3602f09 100644 --- a/Mathlib/RingTheory/DedekindDomain/Instances.lean +++ b/Mathlib/RingTheory/DedekindDomain/Instances.lean @@ -144,10 +144,12 @@ instance : IsScalarTower Rₚ Sₚ L := by RingHom.comp_assoc, ← IsScalarTower.algebraMap_eq, ← IsScalarTower.algebraMap_eq, ← IsScalarTower.algebraMap_eq] +set_option linter.overlappingInstances false in instance [IsDedekindDomain S] : IsDedekindDomain Sₚ := isDedekindDomain S (algebraMapSubmonoid_le_nonZeroDivisors_of_faithfulSMul _ P.primeCompl_le_nonZeroDivisors) _ +set_option linter.overlappingInstances false in instance [IsDedekindDomain R] [IsDedekindDomain S] [Module.Finite R S] [hP : NeZero P] : IsPrincipalIdealRing Sₚ := IsDedekindDomain.isPrincipalIdealRing_localization_over_prime S P (fun h ↦ hP.1 h) diff --git a/Mathlib/RingTheory/DedekindDomain/IntegralClosure.lean b/Mathlib/RingTheory/DedekindDomain/IntegralClosure.lean index 666fb91e0b63e1..e8acc52a4cb729 100644 --- a/Mathlib/RingTheory/DedekindDomain/IntegralClosure.lean +++ b/Mathlib/RingTheory/DedekindDomain/IntegralClosure.lean @@ -208,6 +208,8 @@ theorem integralClosure.isNoetherianRing [IsIntegrallyClosed A] [IsNoetherianRin variable (A K) [IsDomain C] +set_option linter.overlappingInstances false + /-- If `L` is a finite separable extension of `K = Frac(A)`, where `A` is a Dedekind domain, the integral closure `C` of `A` in `L` is a Dedekind domain. diff --git a/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean b/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean index 16a70a0fded7bb..5ba3d87b64bfce 100644 --- a/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean +++ b/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean @@ -78,6 +78,8 @@ variable [IsDomain A] [IsDedekindDomain B] [IsDedekindDomain R₁] [IsDedekindDo namespace IsDedekindDomain +set_option linter.overlappingInstances false + theorem differentIdeal_dvd_map_differentIdeal [Algebra.IsIntegral R₂ B] [Module.Free A R₂] [IsLocalization (Algebra.algebraMapSubmonoid R₂ A⁰) F₂] (h₁ : F₁.LinearDisjoint F₂) (h₂ : F₁ ⊔ F₂ = ⊤) : diff --git a/Mathlib/RingTheory/DiscreteValuationRing/TFAE.lean b/Mathlib/RingTheory/DiscreteValuationRing/TFAE.lean index 78364d2f2209ef..eb9872b71549ab 100644 --- a/Mathlib/RingTheory/DiscreteValuationRing/TFAE.lean +++ b/Mathlib/RingTheory/DiscreteValuationRing/TFAE.lean @@ -92,8 +92,8 @@ theorem exists_maximalIdeal_pow_eq_of_principal [IsNoetherianRing R] [IsLocalRin · rw [hx, Ideal.span_singleton_pow, Ideal.span_le, Set.singleton_subset_iff] exact Nat.find_spec this -theorem maximalIdeal_isPrincipal_of_isDedekindDomain [IsLocalRing R] [IsDomain R] - [IsDedekindDomain R] : (maximalIdeal R).IsPrincipal := by +theorem maximalIdeal_isPrincipal_of_isDedekindDomain [IsLocalRing R] [IsDedekindDomain R] : + (maximalIdeal R).IsPrincipal := by classical by_cases ne_bot : maximalIdeal R = ⊥ · rw [ne_bot]; infer_instance diff --git a/Mathlib/RingTheory/FractionalIdeal/Extended.lean b/Mathlib/RingTheory/FractionalIdeal/Extended.lean index 811bd34b285768..13c8029f0c0d5e 100644 --- a/Mathlib/RingTheory/FractionalIdeal/Extended.lean +++ b/Mathlib/RingTheory/FractionalIdeal/Extended.lean @@ -244,6 +244,8 @@ theorem extendedHom_le_one_iff [IsIntegrallyClosed A] [IsIntegrallyClosed B] : section IsDedekindDomain +set_option linter.overlappingInstances false + variable [IsDedekindDomain A] [IsDedekindDomain B] theorem one_le_extendedHom_iff (hI : I ≠ 0) : 1 ≤ extendedHom L B I ↔ 1 ≤ I := by diff --git a/Mathlib/RingTheory/HahnSeries/Lex.lean b/Mathlib/RingTheory/HahnSeries/Lex.lean index 24fc842b4477a6..10b173159ff1f7 100644 --- a/Mathlib/RingTheory/HahnSeries/Lex.lean +++ b/Mathlib/RingTheory/HahnSeries/Lex.lean @@ -389,7 +389,7 @@ instance [IsOrderedRing R] [NoZeroDivisors R] : IsOrderedRing (Lex R⟦Γ⟧) wh · rwa [leadingCoeff_nonneg_iff] · simpa -instance [IsDomain R] [IsStrictOrderedRing R] : IsStrictOrderedRing (Lex R⟦Γ⟧) where +instance [IsStrictOrderedRing R] : IsStrictOrderedRing (Lex R⟦Γ⟧) where end OrderedRing diff --git a/Mathlib/RingTheory/Ideal/Norm/AbsNorm.lean b/Mathlib/RingTheory/Ideal/Norm/AbsNorm.lean index 5f82d14c60acb5..1277505700fb7f 100644 --- a/Mathlib/RingTheory/Ideal/Norm/AbsNorm.lean +++ b/Mathlib/RingTheory/Ideal/Norm/AbsNorm.lean @@ -197,7 +197,7 @@ theorem cardQuot_mul [IsDedekindDomain S] [Module.Free ℤ S] (I J : Ideal S) : (hIJ (Ideal.dvd_iff_le.mpr le_sup_left) (Ideal.dvd_iff_le.mpr le_sup_right))) /-- The absolute norm of the ideal `I : Ideal R` is the cardinality of the quotient `R ⧸ I`. -/ -noncomputable def Ideal.absNorm [Nontrivial S] [IsDedekindDomain S] [Module.Free ℤ S] : +noncomputable def Ideal.absNorm [IsDedekindDomain S] [Module.Free ℤ S] : Ideal S →*₀ ℕ where toFun := Submodule.cardQuot map_mul' I J := by rw [cardQuot_mul] @@ -208,7 +208,7 @@ noncomputable def Ideal.absNorm [Nontrivial S] [IsDedekindDomain S] [Module.Free namespace Ideal -variable [Nontrivial S] [IsDedekindDomain S] [Module.Free ℤ S] +variable [IsDedekindDomain S] [Module.Free ℤ S] theorem absNorm_apply (I : Ideal S) : absNorm I = cardQuot I := rfl diff --git a/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean b/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean index 2d626bddcdef88..a668d5f1f2b8e1 100644 --- a/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean +++ b/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean @@ -192,6 +192,8 @@ theorem spanNorm_le_comap (I : Ideal S) : spanNorm R I ≤ comap (algebraMap R S | add _ _ _ _ hx hy => exact Submodule.add_mem _ hx hy | smul _ _ _ hx => exact Submodule.smul_mem _ _ hx +set_option linter.overlappingInstances false + /-- Multiplicativity of `Ideal.spanNorm`. simp-normal form is `map_mul (Ideal.relNorm R)`. -/ theorem spanNorm_mul [IsDedekindDomain R] [IsDedekindDomain S] (I J : Ideal S) : spanNorm R (I * J) = spanNorm R I * spanNorm R J := by diff --git a/Mathlib/RingTheory/NormalClosure.lean b/Mathlib/RingTheory/NormalClosure.lean index e412603b1c5bab..03624140bf835a 100644 --- a/Mathlib/RingTheory/NormalClosure.lean +++ b/Mathlib/RingTheory/NormalClosure.lean @@ -126,6 +126,8 @@ instance : IsGalois K (FractionRing T) := by variable [IsDedekindDomain S] +set_option linter.overlappingInstances false + instance : Module.Finite S T := IsIntegralClosure.finite S L E T diff --git a/Mathlib/Tactic/NormNum/Ineq.lean b/Mathlib/Tactic/NormNum/Ineq.lean index 99571399a7cec0..74518ce20e9761 100644 --- a/Mathlib/Tactic/NormNum/Ineq.lean +++ b/Mathlib/Tactic/NormNum/Ineq.lean @@ -89,7 +89,7 @@ theorem isNNRat_le_true [Semiring α] [LinearOrder α] [IsStrictOrderedRing α] simp only [Nat.mul_eq, Nat.cast_mul, mul_invOf_cancel_right'] at h rwa [Nat.commute_cast] at h -theorem isNNRat_lt_true [Semiring α] [LinearOrder α] [IsStrictOrderedRing α] [Nontrivial α] : +theorem isNNRat_lt_true [Semiring α] [LinearOrder α] [IsStrictOrderedRing α] : {a b : α} → {na nb : ℕ} → {da db : ℕ} → IsNNRat a na da → IsNNRat b nb db → decide (na * db < nb * da) → a < b | _, _, _, _, da, db, ⟨_, rfl⟩, ⟨_, rfl⟩, h => by @@ -101,7 +101,7 @@ theorem isNNRat_lt_true [Semiring α] [LinearOrder α] [IsStrictOrderedRing α] simp? at h says simp only [Nat.cast_mul, mul_invOf_cancel_right'] at h rwa [Nat.commute_cast] at h -theorem isNNRat_le_false [Semiring α] [LinearOrder α] [IsStrictOrderedRing α] [Nontrivial α] +theorem isNNRat_le_false [Semiring α] [LinearOrder α] [IsStrictOrderedRing α] {a b : α} {na nb : ℕ} {da db : ℕ} (ha : IsNNRat a na da) (hb : IsNNRat b nb db) (h : decide (nb * da < na * db)) : ¬a ≤ b := not_le_of_gt (isNNRat_lt_true hb ha h) @@ -125,7 +125,7 @@ theorem isRat_le_true [Ring α] [LinearOrder α] [IsStrictOrderedRing α] : mul_invOf_cancel_right'] at h rwa [Int.commute_cast] at h -theorem isRat_lt_true [Ring α] [LinearOrder α] [IsStrictOrderedRing α] [Nontrivial α] : +theorem isRat_lt_true [Ring α] [LinearOrder α] [IsStrictOrderedRing α] : {a b : α} → {na nb : ℤ} → {da db : ℕ} → IsRat a na da → IsRat b nb db → decide (na * db < nb * da) → a < b | _, _, _, _, da, db, ⟨_, rfl⟩, ⟨_, rfl⟩, h => by @@ -137,7 +137,7 @@ theorem isRat_lt_true [Ring α] [LinearOrder α] [IsStrictOrderedRing α] [Nontr simp? at h says simp only [Int.cast_mul, Int.cast_natCast, mul_invOf_cancel_right'] at h rwa [Int.commute_cast] at h -theorem isRat_le_false [Ring α] [LinearOrder α] [IsStrictOrderedRing α] [Nontrivial α] +theorem isRat_le_false [Ring α] [LinearOrder α] [IsStrictOrderedRing α] {a b : α} {na nb : ℤ} {da db : ℕ} (ha : IsRat a na da) (hb : IsRat b nb db) (h : decide (nb * da < na * db)) : ¬a ≤ b := not_le_of_gt (isRat_lt_true hb ha h) diff --git a/Mathlib/Topology/Algebra/Nonarchimedean/Completion.lean b/Mathlib/Topology/Algebra/Nonarchimedean/Completion.lean index d84352703a98a9..e1461995043d87 100644 --- a/Mathlib/Topology/Algebra/Nonarchimedean/Completion.lean +++ b/Mathlib/Topology/Algebra/Nonarchimedean/Completion.lean @@ -64,7 +64,6 @@ instance {G : Type*} [AddGroup G] [UniformSpace G] [IsUniformAddGroup G] exact closure_minimal (Set.image_subset_iff.mpr hCW) C_closed /-- The completion of a nonarchimedean ring is a nonarchimedean ring. -/ -instance {R : Type*} [Ring R] [UniformSpace R] [IsTopologicalRing R] [IsUniformAddGroup R] - [NonarchimedeanRing R] : +instance {R : Type*} [Ring R] [UniformSpace R] [IsUniformAddGroup R] [NonarchimedeanRing R] : NonarchimedeanRing (Completion R) where is_nonarchimedean := NonarchimedeanAddGroup.is_nonarchimedean From 3a07663979d73c5d3cd596bbea4d5c588001b84d Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 1 May 2026 13:52:40 +0100 Subject: [PATCH 111/141] update output text of the linter, depending on whether it is a `Prop` or data --- .../Tactic/Linter/OverlappingInstances.lean | 16 +++++++---- MathlibTest/OverlappingInstances.lean | 28 +++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index be0193a1faed9b..2fe83adc49edff 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -154,25 +154,29 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let mut msgs := #[] for (fvars, overlaps) in sortedOverlaps do let fvarTypes ← fvars.mapM (do instantiateMVars <| ← ·.getType) + -- If the overlapping instances are the same, use a simple message. if fvarTypes.all (· == fvarTypes[0]!) then msgs := msgs.push <| m!"There are {fvarTypes.size} `{.sbracket fvarTypes[0]!}` instances." else + -- `fvarTypes` are either all propositions, or all non-propositions + let isProp ← isProp fvarTypes[0]! let localInsts := (← getLCtx).decls.toList.reduceOption + -- Otherwise, figure out which instances can be synthesized from the other instances let mut redundant := #[] for fvar in fvars, type in fvarTypes do let localInsts := localInsts.filter (·.fvarId != fvar) if (← withLocalInstances localInsts (trySynthInstance type)) matches .some _ then redundant := redundant.push type - if redundant.isEmpty then - if ← isProp fvarTypes[0]! then - continue + -- For `Prop` instances, only warn if there is an instance that can be removed. + if isProp && redundant.isEmpty then + continue let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") - let mut msg := m!"{fvarTypes} give different instances of {overlaps}." + let mut msg := + m!"{fvarTypes} {ite isProp "each imply" "give conflicting instances of"} {overlaps}." unless redundant.isEmpty do let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") - msg := - m!"{msg}\nOut of these, {redundant'} {ite (redundant.size = 1) "is" "are"} redundant." + msg := m!"{msg}\nOf these, {redundant'} {ite (redundant.size = 1) "is" "are"} redundant." msgs := msgs.push msg if msgs.isEmpty then return none diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 1c19457fe787e2..456c0f720a08b3 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -47,7 +47,7 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by @ +3:21...+4:12 warning: Declaration `foo₁` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -63,8 +63,8 @@ set_option linter.overlappingInstances true in warning: Declaration `foo₂` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. - Out of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` are redundant. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. + Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` are redundant. Consider choosing different instance hypotheses. @@ -89,8 +89,8 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true warning: Declaration `foo₄` has overlapping instances: • There are 2 `[FooBarBaz Nat]` instances. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give different instances of `[SubBar Nat]`. - Out of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` are redundant. +• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `[SubBar Nat]`. + Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` are redundant. Consider choosing different instance hypotheses. @@ -102,7 +102,7 @@ theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- warning: Declaration `foo₅` has overlapping instances: -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]` and `[SubBar Nat]`. +`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]` and `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -114,8 +114,8 @@ lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial /-- warning: Declaration `foo₆` has overlapping instances: -• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give different instances of `[Baz Nat]`. -• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give different instances of `[SubBar Nat]`. +• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]`. +• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. Consider choosing different instance hypotheses. @@ -153,8 +153,8 @@ class IsBaz : Prop extends IsBar /-- warning: Declaration `_example` has overlapping instances: -`[IsBar]` and `[IsBaz]` give different instances of `[IsFoo]`. -Out of these, `[IsBar]` is redundant. +`[IsBar]` and `[IsBaz]` each imply `[IsFoo]`. +Of these, `[IsBar]` is redundant. Consider choosing different instance hypotheses. @@ -259,8 +259,8 @@ class B (α : Type u) extends A α /-- warning: Declaration `_example` has overlapping instances: -`[B α]` and `[A α]` give different instances of `[A α]`. -Out of these, `[A α]` is redundant. +`[B α]` and `[A α]` give conflicting instances of `[A α]`. +Of these, `[A α]` is redundant. Consider choosing different instance hypotheses. @@ -286,8 +286,8 @@ class B' (α β : Type*) [A' α] extends B α β where /-- warning: Declaration `_example` has overlapping instances: -`[B α β]` and `[B' α β]` give different instances of `[B α β]`. -Out of these, `[B α β]` is redundant. +`[B α β]` and `[B' α β]` give conflicting instances of `[B α β]`. +Of these, `[B α β]` is redundant. Consider choosing different instance hypotheses. From 9cdda04f4e30fd6d226e1fa78fe4356c2470b1c2 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 1 May 2026 23:57:24 +0100 Subject: [PATCH 112/141] . --- Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean b/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean index 5ba3d87b64bfce..7e78e7fb10fafe 100644 --- a/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean +++ b/Mathlib/RingTheory/DedekindDomain/LinearDisjoint.lean @@ -76,10 +76,10 @@ variable [IsDomain A] [IsDedekindDomain B] [IsDedekindDomain R₁] [IsDedekindDo [IsFractionRing B L] [IsFractionRing R₁ F₁] [IsFractionRing R₂ F₂] [IsIntegrallyClosed A] [IsIntegralClosure B R₁ L] [IsTorsionFree R₁ B] [IsTorsionFree R₂ B] -namespace IsDedekindDomain - set_option linter.overlappingInstances false +namespace IsDedekindDomain + theorem differentIdeal_dvd_map_differentIdeal [Algebra.IsIntegral R₂ B] [Module.Free A R₂] [IsLocalization (Algebra.algebraMapSubmonoid R₂ A⁰) F₂] (h₁ : F₁.LinearDisjoint F₂) (h₂ : F₁ ⊔ F₂ = ⊤) : From 5d7404c59f15f502f20c48f445d001559e6e7940 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sat, 2 May 2026 09:59:35 +0100 Subject: [PATCH 113/141] fix test --- MathlibTest/norm_num.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MathlibTest/norm_num.lean b/MathlibTest/norm_num.lean index 9dcfce2cf50b70..e7ec5ae0f5bdc6 100644 --- a/MathlibTest/norm_num.lean +++ b/MathlibTest/norm_num.lean @@ -333,7 +333,7 @@ end LinearOrderedRing section Rat -variable [Field α] [LinearOrder α] [IsStrictOrderedRing α] [Nontrivial α] +variable [Field α] [LinearOrder α] [IsStrictOrderedRing α] -- Normalize to True example : (1 : ℚ) ≤ 1 := by norm_num1 From 96d6ffff3ae1961aa713953653f0f585c252b5b3 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sat, 2 May 2026 15:25:49 +0100 Subject: [PATCH 114/141] new adaptation --- Mathlib/RingTheory/LocalRing/ResidueField/Instances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/RingTheory/LocalRing/ResidueField/Instances.lean b/Mathlib/RingTheory/LocalRing/ResidueField/Instances.lean index 6e26c40fe75874..f3c37bcdb40462 100644 --- a/Mathlib/RingTheory/LocalRing/ResidueField/Instances.lean +++ b/Mathlib/RingTheory/LocalRing/ResidueField/Instances.lean @@ -35,7 +35,7 @@ instance [Algebra.IsSeparable (A ⧸ p) (B ⧸ q)] : ext x simp [RingHom.algebraMap_toAlgebra, ← IsScalarTower.algebraMap_apply] -instance [p.IsMaximal] [q.IsMaximal] [Algebra.IsSeparable p.ResidueField q.ResidueField] : +instance [Algebra.IsSeparable p.ResidueField q.ResidueField] : Algebra.IsSeparable (A ⧸ p) (B ⧸ q) := by refine Algebra.IsSeparable.of_equiv_equiv (.symm <| .ofBijective _ p.bijective_algebraMap_quotient_residueField) From f6c15fe8ce920de3a2d76ea44193bba314fbf5a4 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sun, 3 May 2026 08:33:28 +0100 Subject: [PATCH 115/141] try profiling in radar? --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 2fe83adc49edff..c0f7eba542cebe 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -205,6 +205,7 @@ def overlappingInstances : Linter where unless getLinterValue linter.overlappingInstances (← getLinterOptions) do return -- Note: we don't break on errors; we want to lint even on partial declarations + profileitM Exception "overlappingInstancesLinter" (← getOptions) do withTraceNode `overlappingInstances (fun _ ↦ return "looking for a local context") do for t in ← getInfoTrees do for (ref, ctx, info) in t.getDeclBodyInfos do From d830fd33ed4629e2da2735b8c267bff1d080126e Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Fri, 8 May 2026 09:22:55 +0100 Subject: [PATCH 116/141] suggestions --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index c0f7eba542cebe..0a65bbf7572488 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -20,10 +20,14 @@ This linter warns against instance diamonds in local contexts. This is a syntax linter. It is run on partially and fully elaborated declarations. -To find diamonds, we compute all parent classes of any given local instance. -For classes that aren't structures, this returns the class itself. -If any of these classes is duplicated, we throw a warning. -This also searches for redundant proposition classes. +To find diamonds, we compute the parent classes of each local instance. +For classes that aren't structures, this is just the class itself. +If any of these parent classes is duplicated, we throw a warning. + +This linter also warns on redundant proposition classes, i.e. those that can be synthesized from +the other instances in the local context. (Note: we do *not* warn on proposition classes that +merely overlap.) Even though redundant proposition classes cause no meaningful issue, they are +still undesirable. A common case where this linter may fire is if the same type class assumption is given in both a `variable` statement and a declaration. This kind of variable shadowing does not actually produce @@ -206,7 +210,6 @@ def overlappingInstances : Linter where return -- Note: we don't break on errors; we want to lint even on partial declarations profileitM Exception "overlappingInstancesLinter" (← getOptions) do - withTraceNode `overlappingInstances (fun _ ↦ return "looking for a local context") do for t in ← getInfoTrees do for (ref, ctx, info) in t.getDeclBodyInfos do let some (lctx, expectedType?) := info.getLCtx? | pure () From aeda3a5c364fa65687ca39a46f816f73a2e5fc0d Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sat, 9 May 2026 12:58:37 +0100 Subject: [PATCH 117/141] fix test for `eraseInstances` --- MathlibTest/OverlappingInstances.lean | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 456c0f720a08b3..a701c3e30b13be 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -273,16 +273,16 @@ end universes namespace parameters -/-! Test a projection that changes the instance parameters. -/ +/-! Test a projection that changes the instance parameters. (This needs `eraseInstances`) -/ class A (α : Type*) where class A' (α : Type*) extends A α where -instance {α} : A α where - class B (α β : Type*) [A α] where class B' (α β : Type*) [A' α] extends B α β where +instance {α} : A α where + /-- warning: Declaration `_example` has overlapping instances: From 778a5fae84ee64cdc4a3fcc5b431c42395e960e7 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sat, 9 May 2026 13:50:56 +0100 Subject: [PATCH 118/141] feat: also warn on overlaps between Prop and data instances --- .../Tactic/Linter/OverlappingInstances.lean | 24 +++--- MathlibTest/OverlappingInstances.lean | 80 +++++++++++++++++-- 2 files changed, 85 insertions(+), 19 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 0a65bbf7572488..821326307cc3ca 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -85,11 +85,13 @@ where if (← get).contains parent then continue modify (·.insert parent) unless ← isInstance info.projFn do continue - unless isProp do - if (← getConstInfo parent).type.getForallBody.isProp then continue + let isProp' := (← getConstInfo parent).type.getForallBody.isProp let proj := Expr.app (mkAppN (.const info.projFn us) type.getAppArgs) inst - acc ← go parent proj acc xs isProp - anyParent := true + acc ← go parent proj acc xs isProp' + -- When projecting from a data-carrying class to a `Prop` class, + -- we record the data-carrying class. This helps with determining which overlaps have data. + unless !isProp && isProp' do + anyParent := true if !anyParent then acc := acc.push (← eraseInstances (type.abstract xs)) return acc @@ -151,9 +153,10 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let overlaps ← findOverlappingInstances if overlaps.isEmpty then return none - let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := - overlaps.fold (init := {}) fun s overlap fvars ↦ s.alter fvars (·.getD #[] |>.push overlap) -- Sort the suggestions in a (somewhat) deterministic way. + let overlaps := overlaps.toArray.qsort (·.1.lt ·.1) + let sortedOverlaps : Std.HashMap (Array FVarId) (Array Expr) := + overlaps.foldl (init := {}) fun s (overlap, fvars) ↦ s.alter fvars (·.getD #[] |>.push overlap) let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] for (fvars, overlaps) in sortedOverlaps do @@ -162,8 +165,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option if fvarTypes.all (· == fvarTypes[0]!) then msgs := msgs.push <| m!"There are {fvarTypes.size} `{.sbracket fvarTypes[0]!}` instances." else - -- `fvarTypes` are either all propositions, or all non-propositions - let isProp ← isProp fvarTypes[0]! + let propOverlap ← overlaps.allM isProp let localInsts := (← getLCtx).decls.toList.reduceOption -- Otherwise, figure out which instances can be synthesized from the other instances let mut redundant := #[] @@ -171,13 +173,13 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let localInsts := localInsts.filter (·.fvarId != fvar) if (← withLocalInstances localInsts (trySynthInstance type)) matches .some _ then redundant := redundant.push type - -- For `Prop` instances, only warn if there is an instance that can be removed. - if isProp && redundant.isEmpty then + -- For `Prop` overlaps, only warn if there is an instance that can be removed. + if propOverlap && redundant.isEmpty then continue let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") let mut msg := - m!"{fvarTypes} {ite isProp "each imply" "give conflicting instances of"} {overlaps}." + m!"{fvarTypes} {ite propOverlap "each imply" "give conflicting instances of"} {overlaps}." unless redundant.isEmpty do let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") msg := m!"{msg}\nOf these, {redundant'} {ite (redundant.size = 1) "is" "are"} redundant." diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index a701c3e30b13be..68509543856e0d 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -3,8 +3,6 @@ module import Mathlib.Tactic.Linter.OverlappingInstances import Mathlib.Init -namespace Lean - public section class SubBar (α : Type) where @@ -142,7 +140,7 @@ private def foo [Add Nat] [Add Nat] : Bool := true end Foo -section prop +namespace prop class IsFoo : Prop @@ -163,12 +161,64 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f #guard_msgs in example [IsBar] [IsBaz] : True := trivial -/-! Don't warn when data overlaps on `Prop` -/ +class Bar : Type extends IsFoo +class Baz : Type extends IsFoo +class Baz1 : Type extends Baz +class Baz2 : Type extends Baz + +/-- +warning: Declaration `_example` has overlapping instances: + +`[IsFoo]` and `[Bar]` each imply `[IsFoo]`. +Of these, `[IsFoo]` is redundant. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example [IsFoo] [Bar] : True := trivial + +example [IsBar] [Bar] : True := trivial + +example [Bar] [Baz] : True := trivial -class IsBarPlus : Type extends IsBar -class IsBazPlus : Type extends IsBaz +/-- +warning: Declaration `_example` has overlapping instances: -example [IsBarPlus] [IsBazPlus] : True := trivial +There are 2 `[Baz]` instances. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example [Baz] [Baz] : True := trivial + +/-- +warning: Declaration `_example` has overlapping instances: + +`[Baz]` and `[Baz1]` give conflicting instances of `[Baz]` and `[IsFoo]`. +Of these, `[Baz]` is redundant. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example [Baz] [Baz1] : True := trivial + +/-- +warning: Declaration `_example` has overlapping instances: + +`[Baz1]` and `[Baz2]` give conflicting instances of `[Baz]` and `[IsFoo]`. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in +example [Baz1] [Baz2] : True := trivial end prop @@ -323,6 +373,20 @@ class FooClass (α : Type) [Add α] [Add α] where structure NotAClass where class IsAClass1 extends NotAClass -class IsAClass2 extends NotAClass +class IsAClass1' extends NotAClass +class IsAClass2 extends IsAClass1 + +example [IsAClass1] [IsAClass1'] : True := trivial + +/-- +warning: Declaration `_example` has overlapping instances: +`[IsAClass1]` and `[IsAClass2]` give conflicting instances of `[IsAClass1]`. +Of these, `[IsAClass1]` is redundant. + +Consider choosing different instance hypotheses. + +Note: This linter can be disabled with `set_option linter.overlappingInstances false` +-/ +#guard_msgs in example [IsAClass1] [IsAClass2] : True := trivial From 6f4646e0680e32953ea3e91cdd94503a49d1daef Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sat, 9 May 2026 14:57:43 +0100 Subject: [PATCH 119/141] adaptations --- Mathlib/Algebra/Colimit/DirectLimit.lean | 4 ++-- Mathlib/Algebra/Module/GradedModule.lean | 7 ++++--- Mathlib/Analysis/CStarAlgebra/Spectrum.lean | 2 -- Mathlib/Analysis/Fourier/FiniteAbelian/Orthogonality.lean | 2 +- .../NumberTheory/NumberField/InfinitePlace/Embeddings.lean | 2 +- Mathlib/RingTheory/Valuation/RankOne.lean | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Mathlib/Algebra/Colimit/DirectLimit.lean b/Mathlib/Algebra/Colimit/DirectLimit.lean index 3c8edf21854754..0e40ad1adf2f09 100644 --- a/Mathlib/Algebra/Colimit/DirectLimit.lean +++ b/Mathlib/Algebra/Colimit/DirectLimit.lean @@ -815,8 +815,8 @@ end NonUnitalStarRing namespace Algebra variable [CommSemiring R] -variable [∀ i, Semiring (G i)] [∀ i j h, RingHomClass (T h) (G i) (G j)] -variable [∀ i, Algebra R (G i)] [∀ i j h, AlgHomClass (T h) R (G i) (G j)] +variable [∀ i, Semiring (G i)] [∀ i, Algebra R (G i)] +variable [∀ i j h, AlgHomClass (T h) R (G i) (G j)] variable [Nonempty ι] variable (G f) in diff --git a/Mathlib/Algebra/Module/GradedModule.lean b/Mathlib/Algebra/Module/GradedModule.lean index 4a8a2e32e392ae..6ca6042130f856 100644 --- a/Mathlib/Algebra/Module/GradedModule.lean +++ b/Mathlib/Algebra/Module/GradedModule.lean @@ -195,20 +195,21 @@ end SetLike namespace GradedModule variable [AddCommMonoid M] [Module A M] [SetLike σ M] [AddSubmonoidClass σ' A] - [AddSubmonoidClass σ M] [SetLike.GradedMonoid 𝓐] [SetLike.GradedSMul 𝓐 𝓜] + [AddSubmonoidClass σ M] [SetLike.GradedSMul 𝓐 𝓜] + [DecidableEq ιA] [DecidableEq ιM] [GradedRing 𝓐] /-- The smul multiplication of `A` on `⨁ i, 𝓜 i` from `(⨁ i, 𝓐 i) →+ (⨁ i, 𝓜 i) →+ ⨁ i, 𝓜 i` turns `⨁ i, 𝓜 i` into an `A`-module -/ @[implicit_reducible] -def isModule [DecidableEq ιA] [DecidableEq ιM] [GradedRing 𝓐] : Module A (⨁ i, 𝓜 i) := +def isModule : Module A (⨁ i, 𝓜 i) := { Module.compHom _ (DirectSum.decomposeRingEquiv 𝓐 : A ≃+* ⨁ i, 𝓐 i).toRingHom with smul := fun a b => DirectSum.decompose 𝓐 a • b } /-- `⨁ i, 𝓜 i` and `M` are isomorphic as `A`-modules. "The internal version" and "the external version" are isomorphism as `A`-modules. -/ -def linearEquiv [DecidableEq ιA] [DecidableEq ιM] [GradedRing 𝓐] [DirectSum.Decomposition 𝓜] : +def linearEquiv [DirectSum.Decomposition 𝓜] : @LinearEquiv A A _ _ (RingHom.id A) (RingHom.id A) _ _ M (⨁ i, 𝓜 i) _ _ _ (by letI := isModule 𝓐 𝓜; infer_instance) := by letI h := isModule 𝓐 𝓜 diff --git a/Mathlib/Analysis/CStarAlgebra/Spectrum.lean b/Mathlib/Analysis/CStarAlgebra/Spectrum.lean index 2f7e5a5c63ebf3..227fbec40ee648 100644 --- a/Mathlib/Analysis/CStarAlgebra/Spectrum.lean +++ b/Mathlib/Analysis/CStarAlgebra/Spectrum.lean @@ -149,8 +149,6 @@ theorem IsStarNormal.spectralRadius_eq_nnnorm (a : A) [IsStarNormal a] : convert tendsto_nhds_unique h₂ (pow_nnnorm_pow_one_div_tendsto_nhds_spectralRadius (a⋆ * a)) rw [(IsSelfAdjoint.star_mul_self a).spectralRadius_eq_nnnorm, sq, nnnorm_star_mul_self, coe_mul] -variable [StarModule ℂ A] - /-- Any element of the spectrum of a selfadjoint is real. -/ theorem IsSelfAdjoint.mem_spectrum_eq_re {a : A} (ha : IsSelfAdjoint a) {z : ℂ} (hz : z ∈ spectrum ℂ a) : z = z.re := by diff --git a/Mathlib/Analysis/Fourier/FiniteAbelian/Orthogonality.lean b/Mathlib/Analysis/Fourier/FiniteAbelian/Orthogonality.lean index a822d8e1b75e34..619b123068af91 100644 --- a/Mathlib/Analysis/Fourier/FiniteAbelian/Orthogonality.lean +++ b/Mathlib/Analysis/Fourier/FiniteAbelian/Orthogonality.lean @@ -30,7 +30,7 @@ section AddGroup variable [AddGroup G] section Semifield -variable [Fintype G] [Semifield R] [IsDomain R] [CharZero R] {ψ : AddChar G R} +variable [Fintype G] [Semifield R] [CharZero R] {ψ : AddChar G R} lemma expect_eq_ite (ψ : AddChar G R) : 𝔼 a, ψ a = if ψ = 0 then 1 else 0 := by simp [Fintype.expect_eq_sum_div_card, sum_eq_ite, ite_div] diff --git a/Mathlib/NumberTheory/NumberField/InfinitePlace/Embeddings.lean b/Mathlib/NumberTheory/NumberField/InfinitePlace/Embeddings.lean index ea6a83b45f797e..bbcbbf9367bbc9 100644 --- a/Mathlib/NumberTheory/NumberField/InfinitePlace/Embeddings.lean +++ b/Mathlib/NumberTheory/NumberField/InfinitePlace/Embeddings.lean @@ -148,7 +148,7 @@ end NumberField.Embeddings section Place -variable {K : Type*} [Field K] {A : Type*} [NormedDivisionRing A] [Nontrivial A] (φ : K →+* A) +variable {K : Type*} [Field K] {A : Type*} [NormedDivisionRing A] (φ : K →+* A) /-- An embedding into a normed division ring defines a place of `K` -/ def NumberField.place : AbsoluteValue K ℝ := diff --git a/Mathlib/RingTheory/Valuation/RankOne.lean b/Mathlib/RingTheory/Valuation/RankOne.lean index 33263f3ff6c1b1..4b42ef70a225c5 100644 --- a/Mathlib/RingTheory/Valuation/RankOne.lean +++ b/Mathlib/RingTheory/Valuation/RankOne.lean @@ -131,7 +131,7 @@ instance : IsNontrivial v where section Restrict -instance isNontrivial_restrict [v.IsNontrivial] : (v.restrict).IsNontrivial where +instance isNontrivial_restrict : (v.restrict).IsNontrivial where exists_val_nontrivial := by obtain ⟨x, ⟨hx0, hx1⟩⟩ := IsNontrivial.exists_val_nontrivial (v := v) exact ⟨x, by simp [hx0], by grind [restrict_eq_one_iff]⟩ From 8a3ea57c68578847ea29b35c4779ca898e8a33f6 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Thu, 14 May 2026 19:41:50 -0400 Subject: [PATCH 120/141] chore: adaptations --- Mathlib/Algebra/Homology/HomotopyFiber.lean | 2 +- .../CategoryTheory/Triangulated/LocalizingSubcategory.lean | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Mathlib/Algebra/Homology/HomotopyFiber.lean b/Mathlib/Algebra/Homology/HomotopyFiber.lean index e5efe421ca2e24..bcb49936da5332 100644 --- a/Mathlib/Algebra/Homology/HomotopyFiber.lean +++ b/Mathlib/Algebra/Homology/HomotopyFiber.lean @@ -42,7 +42,7 @@ class HasHomotopyFiber (φ : F ⟶ G) : Prop where instance [HasBinaryBiproducts C] : HasHomotopyFiber φ where hasBinaryBiproduct _ _ _ := inferInstance -variable [HasHomotopyFiber φ] [DecidableRel c.Rel] +variable [HasHomotopyFiber φ] instance : HasHomotopyCofiber ((opFunctor C c).map φ.op) where hasBinaryBiproduct i j hij := by diff --git a/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean b/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean index 9c8d3d42ec0c5b..38dca9bf636293 100644 --- a/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean +++ b/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean @@ -303,8 +303,7 @@ example : ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Full example : ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Faithful := by infer_instance -instance [A.IsVerdierLeftLocalizing B] [Preadditive D₁] [Preadditive D₂] - [L₁.Additive] [L₂.Additive] : +instance [Preadditive D₁] [Preadditive D₂] [L₁.Additive] [L₂.Additive] : ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Additive := by let F := (A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂ rw [Localization.functor_additive_iff L₁ (B.inverseImage A.ι).trW] @@ -316,7 +315,7 @@ instance [A.IsVerdierLeftLocalizing B] [Preadditive D₁] [Preadditive D₂] then the induced functor between the localizations with respect to `(B.inverseImage A.ι).trW` and `B.trW` is fully faithful. -/ @[no_expose] -noncomputable def IsVerdierLeftLocalizing.fullyFaithful [A.IsVerdierLeftLocalizing B] +noncomputable def IsVerdierLeftLocalizing.fullyFaithful {L₁ : A.FullSubcategory ⥤ D₁} {L₂ : C ⥤ D₂} {F : D₁ ⥤ D₂} [L₁.IsLocalization (B.inverseImage A.ι).trW] [L₂.IsLocalization B.trW] (e : L₁ ⋙ F ≅ A.ι ⋙ L₂) : From 1864b7a6c952ba86786bced4c0ac404ba5df21df Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Mon, 18 May 2026 16:28:30 +0100 Subject: [PATCH 121/141] review suggestions --- .../Tactic/Linter/OverlappingInstances.lean | 18 +++- MathlibTest/OverlappingInstances.lean | 88 ++++++------------- 2 files changed, 43 insertions(+), 63 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 821326307cc3ca..c7245a1a073ec0 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -159,13 +159,17 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option overlaps.foldl (init := {}) fun s (overlap, fvars) ↦ s.alter fvars (·.getD #[] |>.push overlap) let sortedOverlaps := sortedOverlaps.toArray.qsort (Array.lex ·.2 ·.2 Expr.lt) let mut msgs := #[] + let mut needsDiamondMsg := false for (fvars, overlaps) in sortedOverlaps do let fvarTypes ← fvars.mapM (do instantiateMVars <| ← ·.getType) -- If the overlapping instances are the same, use a simple message. if fvarTypes.all (· == fvarTypes[0]!) then - msgs := msgs.push <| m!"There are {fvarTypes.size} `{.sbracket fvarTypes[0]!}` instances." + msgs := msgs.push <| + m!"There are {fvarTypes.size} `{.sbracket fvarTypes[0]!}` instances; one is sufficient." else let propOverlap ← overlaps.allM isProp + -- Ignore `Prop` overlaps when data conflicts are present. + let overlaps ← if !propOverlap then overlaps.filterM (notM <| isProp ·) else pure overlaps let localInsts := (← getLCtx).decls.toList.reduceOption -- Otherwise, figure out which instances can be synthesized from the other instances let mut redundant := #[] @@ -180,9 +184,11 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") let mut msg := m!"{fvarTypes} {ite propOverlap "each imply" "give conflicting instances of"} {overlaps}." - unless redundant.isEmpty do + if redundant.isEmpty then + needsDiamondMsg := true + else let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") - msg := m!"{msg}\nOf these, {redundant'} {ite (redundant.size = 1) "is" "are"} redundant." + msg := m!"{msg}\nOf these, {redundant'} may be removed." msgs := msgs.push msg if msgs.isEmpty then return none @@ -197,7 +203,11 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option -- Create a bulleted list if there are multiple messages, otherwise just a single line msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {.nestD ·}") - msg := msg ++ m!"\n\nConsider choosing different instance hypotheses." + if needsDiamondMsg then + msg := msg ++ m!"\n\n\ + When two instances of a type class are not definitionally equal to eachother, \ + they form an \"instance diamond\", which can lead to unexpected unification failures.\n\ + Consider assuming different instances." addMessageContextFull msg initialize registerTraceClass `overlappingInstances diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 68509543856e0d..611972b9086e11 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -30,9 +30,7 @@ inst✝¹ inst✝ : Add Nat --- warning: Declaration `foo` has overlapping instances: -There are 4 `[Add Nat]` instances. - -Consider choosing different instance hypotheses. +There are 4 `[Add Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -47,7 +45,8 @@ warning: Declaration `foo₁` has overlapping instances: `[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. -Consider choosing different instance hypotheses. +When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. +Consider assuming different instances. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -60,11 +59,9 @@ set_option linter.overlappingInstances true in /-- warning: Declaration `foo₂` has overlapping instances: -• There are 2 `[FooBarBaz Nat]` instances. +• There are 2 `[FooBarBaz Nat]` instances; one is sufficient. • `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. - Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` are redundant. - -Consider choosing different instance hypotheses. + Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -74,9 +71,7 @@ def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- warning: Declaration `foo₃` has overlapping instances: -There are 2 `[FooBarBaz Nat]` instances. - -Consider choosing different instance hypotheses. +There are 2 `[FooBarBaz Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -86,11 +81,9 @@ def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- warning: Declaration `foo₄` has overlapping instances: -• There are 2 `[FooBarBaz Nat]` instances. +• There are 2 `[FooBarBaz Nat]` instances; one is sufficient. • `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `[SubBar Nat]`. - Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` are redundant. - -Consider choosing different instance hypotheses. + Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -102,7 +95,8 @@ warning: Declaration `foo₅` has overlapping instances: `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]` and `[SubBar Nat]`. -Consider choosing different instance hypotheses. +When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. +Consider assuming different instances. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -115,7 +109,8 @@ warning: Declaration `foo₆` has overlapping instances: • `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]`. • `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. -Consider choosing different instance hypotheses. +When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. +Consider assuming different instances. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -129,9 +124,7 @@ namespace Foo /-- warning: Declaration `foo` has overlapping instances: -There are 2 `[Add Nat]` instances. - -Consider choosing different instance hypotheses. +There are 2 `[Add Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -152,9 +145,7 @@ class IsBaz : Prop extends IsBar warning: Declaration `_example` has overlapping instances: `[IsBar]` and `[IsBaz]` each imply `[IsFoo]`. -Of these, `[IsBar]` is redundant. - -Consider choosing different instance hypotheses. +Of these, `[IsBar]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -170,9 +161,7 @@ class Baz2 : Type extends Baz warning: Declaration `_example` has overlapping instances: `[IsFoo]` and `[Bar]` each imply `[IsFoo]`. -Of these, `[IsFoo]` is redundant. - -Consider choosing different instance hypotheses. +Of these, `[IsFoo]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -186,9 +175,7 @@ example [Bar] [Baz] : True := trivial /-- warning: Declaration `_example` has overlapping instances: -There are 2 `[Baz]` instances. - -Consider choosing different instance hypotheses. +There are 2 `[Baz]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -198,10 +185,8 @@ example [Baz] [Baz] : True := trivial /-- warning: Declaration `_example` has overlapping instances: -`[Baz]` and `[Baz1]` give conflicting instances of `[Baz]` and `[IsFoo]`. -Of these, `[Baz]` is redundant. - -Consider choosing different instance hypotheses. +`[Baz]` and `[Baz1]` give conflicting instances of `[Baz]`. +Of these, `[Baz]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -211,9 +196,10 @@ example [Baz] [Baz1] : True := trivial /-- warning: Declaration `_example` has overlapping instances: -`[Baz1]` and `[Baz2]` give conflicting instances of `[Baz]` and `[IsFoo]`. +`[Baz1]` and `[Baz2]` give conflicting instances of `[Baz]`. -Consider choosing different instance hypotheses. +When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. +Consider assuming different instances. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -232,9 +218,7 @@ class inductive IndFoo where /-- warning: Declaration `indFoo` has overlapping instances: -There are 2 `[IndFoo]` instances. - -Consider choosing different instance hypotheses. +There are 2 `[IndFoo]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -247,9 +231,7 @@ class inductive IndFooProp : Prop where /-- warning: Declaration `indFooProp` has overlapping instances: -There are 2 `[IndFooProp]` instances. - -Consider choosing different instance hypotheses. +There are 2 `[IndFooProp]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -265,9 +247,7 @@ variable {α : Type*} [Repr α] /-- warning: Declaration `needsInstantiateMVars` has overlapping instances: -There are 2 `[Repr α]` instances. - -Consider choosing different instance hypotheses. +There are 2 `[Repr α]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -286,9 +266,7 @@ set_option linter.overlappingInstances false /-- warning: Declaration `fooSomething` has overlapping instances: -There are 4 `[Add Nat]` instances. - -Consider choosing different instance hypotheses. +There are 4 `[Add Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -310,9 +288,7 @@ class B (α : Type u) extends A α warning: Declaration `_example` has overlapping instances: `[B α]` and `[A α]` give conflicting instances of `[A α]`. -Of these, `[A α]` is redundant. - -Consider choosing different instance hypotheses. +Of these, `[A α]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -337,9 +313,7 @@ instance {α} : A α where warning: Declaration `_example` has overlapping instances: `[B α β]` and `[B' α β]` give conflicting instances of `[B α β]`. -Of these, `[B α β]` is redundant. - -Consider choosing different instance hypotheses. +Of these, `[B α β]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -353,9 +327,7 @@ end parameters /-- warning: Declaration `List.lt'.go` has overlapping instances: -There are 2 `[DecidableEq α]` instances. - -Consider choosing different instance hypotheses. +There are 2 `[DecidableEq α]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -382,9 +354,7 @@ example [IsAClass1] [IsAClass1'] : True := trivial warning: Declaration `_example` has overlapping instances: `[IsAClass1]` and `[IsAClass2]` give conflicting instances of `[IsAClass1]`. -Of these, `[IsAClass1]` is redundant. - -Consider choosing different instance hypotheses. +Of these, `[IsAClass1]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ From 59377c1c379479d6164d39f8ef31b54674c23956 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com> Date: Mon, 18 May 2026 16:45:51 +0100 Subject: [PATCH 122/141] Update Mathlib/Tactic/Linter/OverlappingInstances.lean Co-authored-by: Thomas R. Murrills <68410468+thorimur@users.noreply.github.com> --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index c7245a1a073ec0..364cb36c61997c 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -205,7 +205,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {.nestD ·}") if needsDiamondMsg then msg := msg ++ m!"\n\n\ - When two instances of a type class are not definitionally equal to eachother, \ + When two instances of a type class are not definitionally equal to each other, \ they form an \"instance diamond\", which can lead to unexpected unification failures.\n\ Consider assuming different instances." addMessageContextFull msg From 4f757d41656d3fa70bbe015157696b682959bd4b Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 20 May 2026 00:59:52 +0100 Subject: [PATCH 123/141] feat: better messages Co-authored-by: MI Retreat attendees --- .../Tactic/Linter/OverlappingInstances.lean | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 364cb36c61997c..8119f26bd23cc4 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -183,31 +183,32 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") let mut msg := - m!"{fvarTypes} {ite propOverlap "each imply" "give conflicting instances of"} {overlaps}." + m!"{fvarTypes} {ite propOverlap "each imply" + "can be used to infer conflicting versions of"} {overlaps}." if redundant.isEmpty then needsDiamondMsg := true else let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") - msg := m!"{msg}\nOf these, {redundant'} may be removed." + msg := m!"{msg}\n💡 Of these, {redundant'} may be removed." msgs := msgs.push msg if msgs.isEmpty then return none - let declDescr ← + let mut msg := ← do if let some decl := ctx.parentDecl? then -- Use `addMessageContextPartial` to clear the local context, -- so as to avoid a name clash with the recursive auxiliary hypothesis of the same name. - pure m!"Declaration `{← addMessageContextPartial (.ofConstName decl)}`" + pure m!"Overlapping instances in `{← addMessageContextPartial (.ofConstName decl)}`:\n" else - pure "The current declaration" - let mut msg := m!"{declDescr} has overlapping instances:" - -- Create a bulleted list if there are multiple messages, otherwise just a single line - msg := if h : msgs.size = 1 then m!"{msg}\n\n{msgs[0]}" else - msgs.foldl (init := msg ++ "\n") (m!"{·}\n• {.nestD ·}") + pure m!"Overlapping instances:" + for overlapMsg in msgs do + msg := msg ++ m!"\n⚠️ {overlapMsg}" if needsDiamondMsg then msg := msg ++ m!"\n\n\ - When two instances of a type class are not definitionally equal to each other, \ - they form an \"instance diamond\", which can lead to unexpected unification failures.\n\ - Consider assuming different instances." + When a data-carrying type class can be inferred from two different type classes in the local + context, there are two incompatible instances of that type class. These form an \"instance + diamond\", which leads to unexpected unification failures.\ + \n\n\ + Restructure your type class arguments to avoid this." addMessageContextFull msg initialize registerTraceClass `overlappingInstances From aa16b05dc47137374596be1757aa11263d1e5446 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 20 May 2026 01:00:52 +0100 Subject: [PATCH 124/141] chore: string continuations --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 8119f26bd23cc4..579001bfd17f87 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -204,8 +204,8 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option msg := msg ++ m!"\n⚠️ {overlapMsg}" if needsDiamondMsg then msg := msg ++ m!"\n\n\ - When a data-carrying type class can be inferred from two different type classes in the local - context, there are two incompatible instances of that type class. These form an \"instance + When a data-carrying type class can be inferred from two different type classes in the local \ + context, there are two incompatible instances of that type class. These form an \"instance \ diamond\", which leads to unexpected unification failures.\ \n\n\ Restructure your type class arguments to avoid this." From 15b9025e5b1af4c8f74b436ab84227ea89180658 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 20 May 2026 01:01:35 +0100 Subject: [PATCH 125/141] chore: update tests --- MathlibTest/OverlappingInstances.lean | 129 +++++++++++++------------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 611972b9086e11..da943fe4fc8eec 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -28,9 +28,9 @@ error: unsolved goals inst✝¹ inst✝ : Add Nat ⊢ [Add Nat] → [Add Nat] → Bool --- -warning: Declaration `foo` has overlapping instances: +warning: Overlapping instances in `foo`: -There are 4 `[Add Nat]` instances; one is sufficient. +⚠️ There are 4 `[Add Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -38,15 +38,15 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by skip - /-- @ +3:21...+4:12 -warning: Declaration `foo₁` has overlapping instances: +warning: Overlapping instances in `foo₁`: + +⚠️ `[FooBarBaz Nat]` and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -`[FooBarBaz Nat]` and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. -When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. -Consider assuming different instances. +Restructure your type class arguments to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -57,11 +57,12 @@ set_option linter.overlappingInstances true in exact true /-- -warning: Declaration `foo₂` has overlapping instances: +warning: Overlapping instances in `foo₂`: -• There are 2 `[FooBarBaz Nat]` instances; one is sufficient. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. - Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` may be removed. +⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. +⚠️ `[FooBarBaz + Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. +💡 Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -69,9 +70,9 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- -warning: Declaration `foo₃` has overlapping instances: +warning: Overlapping instances in `foo₃`: -There are 2 `[FooBarBaz Nat]` instances; one is sufficient. +⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -79,11 +80,11 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- -warning: Declaration `foo₄` has overlapping instances: +warning: Overlapping instances in `foo₄`: -• There are 2 `[FooBarBaz Nat]` instances; one is sufficient. -• `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` give conflicting instances of `[SubBar Nat]`. - Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` may be removed. +⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. +⚠️ `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. +💡 Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -91,12 +92,13 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- -warning: Declaration `foo₅` has overlapping instances: +warning: Overlapping instances in `foo₅`: + +⚠️ `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` can be used to infer conflicting versions of `[Baz Nat]` and `[SubBar Nat]`. -`[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]` and `[SubBar Nat]`. +When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. -When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. -Consider assuming different instances. +Restructure your type class arguments to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -104,13 +106,15 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial /-- -warning: Declaration `foo₆` has overlapping instances: +warning: Overlapping instances in `foo₆`: -• `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` give conflicting instances of `[Baz Nat]`. -• `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` give conflicting instances of `[SubBar Nat]`. +⚠️ `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` can be used to infer conflicting versions of `[Baz Nat]`. +⚠️ `[FooBarBaz + Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. -Consider assuming different instances. +When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. + +Restructure your type class arguments to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -122,9 +126,9 @@ namespace Foo /-! Test unresolving name (`foo`, not `Foo.foo` or `_private...foo`) -/ /-- -warning: Declaration `foo` has overlapping instances: +warning: Overlapping instances in `foo`: -There are 2 `[Add Nat]` instances; one is sufficient. +⚠️ There are 2 `[Add Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -142,10 +146,10 @@ class IsBar : Prop extends IsFoo class IsBaz : Prop extends IsBar /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: -`[IsBar]` and `[IsBaz]` each imply `[IsFoo]`. -Of these, `[IsBar]` may be removed. +⚠️ `[IsBar]` and `[IsBaz]` each imply `[IsFoo]`. +💡 Of these, `[IsBar]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -158,10 +162,10 @@ class Baz1 : Type extends Baz class Baz2 : Type extends Baz /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: -`[IsFoo]` and `[Bar]` each imply `[IsFoo]`. -Of these, `[IsFoo]` may be removed. +⚠️ `[IsFoo]` and `[Bar]` each imply `[IsFoo]`. +💡 Of these, `[IsFoo]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -173,9 +177,9 @@ example [IsBar] [Bar] : True := trivial example [Bar] [Baz] : True := trivial /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: -There are 2 `[Baz]` instances; one is sufficient. +⚠️ There are 2 `[Baz]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -183,10 +187,10 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f example [Baz] [Baz] : True := trivial /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: -`[Baz]` and `[Baz1]` give conflicting instances of `[Baz]`. -Of these, `[Baz]` may be removed. +⚠️ `[Baz]` and `[Baz1]` can be used to infer conflicting versions of `[Baz]`. +💡 Of these, `[Baz]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -194,12 +198,13 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f example [Baz] [Baz1] : True := trivial /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: + +⚠️ `[Baz1]` and `[Baz2]` can be used to infer conflicting versions of `[Baz]`. -`[Baz1]` and `[Baz2]` give conflicting instances of `[Baz]`. +When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. -When two instances of a type class are not definitionally equal to eachother, they form an "instance diamond", which can lead to unexpected unification failures. -Consider assuming different instances. +Restructure your type class arguments to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -216,9 +221,9 @@ class inductive IndFoo where | mk₁ (n : Nat) | mk₂ (b : Bool) /-- -warning: Declaration `indFoo` has overlapping instances: +warning: Overlapping instances in `indFoo`: -There are 2 `[IndFoo]` instances; one is sufficient. +⚠️ There are 2 `[IndFoo]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -229,9 +234,9 @@ class inductive IndFooProp : Prop where | mk₁ (n : Nat) | mk₂ (b : Bool) /-- -warning: Declaration `indFooProp` has overlapping instances: +warning: Overlapping instances in `indFooProp`: -There are 2 `[IndFooProp]` instances; one is sufficient. +⚠️ There are 2 `[IndFooProp]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -245,9 +250,9 @@ section instantiateMVars variable {α : Type*} [Repr α] /-- -warning: Declaration `needsInstantiateMVars` has overlapping instances: +warning: Overlapping instances in `needsInstantiateMVars`: -There are 2 `[Repr α]` instances; one is sufficient. +⚠️ There are 2 `[Repr α]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -264,9 +269,9 @@ def fooNothing [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := true set_option linter.overlappingInstances false /-- -warning: Declaration `fooSomething` has overlapping instances: +warning: Overlapping instances in `fooSomething`: -There are 4 `[Add Nat]` instances; one is sufficient. +⚠️ There are 4 `[Add Nat]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -285,10 +290,10 @@ class A (α : Sort u) where class B (α : Type u) extends A α /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: -`[B α]` and `[A α]` give conflicting instances of `[A α]`. -Of these, `[A α]` may be removed. +⚠️ `[B α]` and `[A α]` can be used to infer conflicting versions of `[A α]`. +💡 Of these, `[A α]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -310,10 +315,10 @@ class B' (α β : Type*) [A' α] extends B α β where instance {α} : A α where /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: -`[B α β]` and `[B' α β]` give conflicting instances of `[B α β]`. -Of these, `[B α β]` may be removed. +⚠️ `[B α β]` and `[B' α β]` can be used to infer conflicting versions of `[B α β]`. +💡 Of these, `[B α β]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -325,9 +330,9 @@ end parameters /-! Test a `where` clause. -/ /-- -warning: Declaration `List.lt'.go` has overlapping instances: +warning: Overlapping instances in `List.lt'.go`: -There are 2 `[DecidableEq α]` instances; one is sufficient. +⚠️ There are 2 `[DecidableEq α]` instances; one is sufficient. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -351,10 +356,10 @@ class IsAClass2 extends IsAClass1 example [IsAClass1] [IsAClass1'] : True := trivial /-- -warning: Declaration `_example` has overlapping instances: +warning: Overlapping instances in `_example`: -`[IsAClass1]` and `[IsAClass2]` give conflicting instances of `[IsAClass1]`. -Of these, `[IsAClass1]` may be removed. +⚠️ `[IsAClass1]` and `[IsAClass2]` can be used to infer conflicting versions of `[IsAClass1]`. +💡 Of these, `[IsAClass1]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ From a877b3f6dc2ba6900430e8911d2be2398bc027bd Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 20 May 2026 16:35:08 +0100 Subject: [PATCH 126/141] =?UTF-8?q?chore:=20update=20unicode=20linter=20to?= =?UTF-8?q?=20allow=20=E2=9A=A0=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean index b4b7a8b9e8c9e3..153d1931e91598 100644 --- a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean +++ b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean @@ -180,7 +180,8 @@ public def emojis : Array Char := #[ .ofNat 0x1F50D, -- 🔍️ .ofNat 0x1F389, -- 🎉️ '\u23F3', -- ⏳️ - .ofNat 0x1F3C1 -- 🏁️ + .ofNat 0x1F3C1, -- 🏁️ + '⚠' -- ⚠️ ] /-- Unicode symbols in mathlib that should always be followed by the text variant selector. -/ From 0bdfe2258ddedf8dc4440cf5e8f226906101f885 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 20 May 2026 16:36:20 +0100 Subject: [PATCH 127/141] chore: unicode selector --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 579001bfd17f87..a24498a4c32ed7 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -189,7 +189,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option needsDiamondMsg := true else let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") - msg := m!"{msg}\n💡 Of these, {redundant'} may be removed." + msg := m!"{msg}\n💡️ Of these, {redundant'} may be removed." msgs := msgs.push msg if msgs.isEmpty then return none From 25632d8599aace2e7e89ccd0eb5ff623eb97cb42 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Wed, 20 May 2026 16:45:49 +0100 Subject: [PATCH 128/141] chore: better unicode linter fix --- Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean index 153d1931e91598..64dcc9fdb3af88 100644 --- a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean +++ b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean @@ -180,7 +180,11 @@ public def emojis : Array Char := #[ .ofNat 0x1F50D, -- 🔍️ .ofNat 0x1F389, -- 🎉️ '\u23F3', -- ⏳️ - .ofNat 0x1F3C1, -- 🏁️ + .ofNat 0x1F3C1, -- 🏁️️ +] + +/-- Unicode symbols in mathlib that may be followed by the emoji variant selector, or may not. -/ +public def maybeEmojis : Array Char := #[ '⚠' -- ⚠️ ] @@ -202,6 +206,7 @@ public def isAllowedCharacter (c : Char) : Bool := || withVSCodeAbbrev.contains c || othersInMathlib.contains c || emojis.contains c + || maybeEmojis.contains c || nonEmojis.contains c || c == UnicodeVariant.emoji || c == UnicodeVariant.text From 8cfa633844748b39ab921b1fb4e8aa1c7ac3ac8a Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 21 May 2026 22:56:01 +0100 Subject: [PATCH 129/141] new rewording --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index a24498a4c32ed7..ba4167ae496861 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -204,11 +204,11 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option msg := msg ++ m!"\n⚠️ {overlapMsg}" if needsDiamondMsg then msg := msg ++ m!"\n\n\ - When a data-carrying type class can be inferred from two different type classes in the local \ - context, there are two incompatible instances of that type class. These form an \"instance \ - diamond\", which leads to unexpected unification failures.\ + When a data-carrying type class has multiple potential instances coming from different \ + instances parameters, then these instances are incompatible. \ + This is an example of \"instance diamond\", which leads to unexpected unification failures.\ \n\n\ - Restructure your type class arguments to avoid this." + Restructure your instance parameters to avoid this." addMessageContextFull msg initialize registerTraceClass `overlappingInstances From 453f413c04b537b9a94b878fc61631aa4c0344bb Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 21 May 2026 23:01:01 +0100 Subject: [PATCH 130/141] fix tests --- MathlibTest/OverlappingInstances.lean | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index da943fe4fc8eec..35bd29a3b2fd30 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -44,9 +44,9 @@ warning: Overlapping instances in `foo₁`: ⚠️ `[FooBarBaz Nat]` and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. -Restructure your type class arguments to avoid this. +Restructure your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -62,7 +62,7 @@ warning: Overlapping instances in `foo₂`: ⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. ⚠️ `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -💡 Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` may be removed. +💡️ Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -84,7 +84,7 @@ warning: Overlapping instances in `foo₄`: ⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. ⚠️ `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -💡 Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` may be removed. +💡️ Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -96,9 +96,9 @@ warning: Overlapping instances in `foo₅`: ⚠️ `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` can be used to infer conflicting versions of `[Baz Nat]` and `[SubBar Nat]`. -When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. -Restructure your type class arguments to avoid this. +Restructure your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -112,9 +112,9 @@ warning: Overlapping instances in `foo₆`: ⚠️ `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. -Restructure your type class arguments to avoid this. +Restructure your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -149,7 +149,7 @@ class IsBaz : Prop extends IsBar warning: Overlapping instances in `_example`: ⚠️ `[IsBar]` and `[IsBaz]` each imply `[IsFoo]`. -💡 Of these, `[IsBar]` may be removed. +💡️ Of these, `[IsBar]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -165,7 +165,7 @@ class Baz2 : Type extends Baz warning: Overlapping instances in `_example`: ⚠️ `[IsFoo]` and `[Bar]` each imply `[IsFoo]`. -💡 Of these, `[IsFoo]` may be removed. +💡️ Of these, `[IsFoo]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -190,7 +190,7 @@ example [Baz] [Baz] : True := trivial warning: Overlapping instances in `_example`: ⚠️ `[Baz]` and `[Baz1]` can be used to infer conflicting versions of `[Baz]`. -💡 Of these, `[Baz]` may be removed. +💡️ Of these, `[Baz]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -202,9 +202,9 @@ warning: Overlapping instances in `_example`: ⚠️ `[Baz1]` and `[Baz2]` can be used to infer conflicting versions of `[Baz]`. -When a data-carrying type class can be inferred from two different type classes in the local context, there are two incompatible instances of that type class. These form an "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. -Restructure your type class arguments to avoid this. +Restructure your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -293,7 +293,7 @@ class B (α : Type u) extends A α warning: Overlapping instances in `_example`: ⚠️ `[B α]` and `[A α]` can be used to infer conflicting versions of `[A α]`. -💡 Of these, `[A α]` may be removed. +💡️ Of these, `[A α]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -318,7 +318,7 @@ instance {α} : A α where warning: Overlapping instances in `_example`: ⚠️ `[B α β]` and `[B' α β]` can be used to infer conflicting versions of `[B α β]`. -💡 Of these, `[B α β]` may be removed. +💡️ Of these, `[B α β]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -359,7 +359,7 @@ example [IsAClass1] [IsAClass1'] : True := trivial warning: Overlapping instances in `_example`: ⚠️ `[IsAClass1]` and `[IsAClass2]` can be used to infer conflicting versions of `[IsAClass1]`. -💡 Of these, `[IsAClass1]` may be removed. +💡️ Of these, `[IsAClass1]` may be removed. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ From 8c92ade1f2830f5f748b65bcd333045167c31c17 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Thu, 21 May 2026 23:03:31 +0100 Subject: [PATCH 131/141] redundant directory dependency exceptions --- Mathlib/Tactic/Linter/DirectoryDependency.lean | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/DirectoryDependency.lean b/Mathlib/Tactic/Linter/DirectoryDependency.lean index 4b3d287f7afdf8..2abecaaa029086 100644 --- a/Mathlib/Tactic/Linter/DirectoryDependency.lean +++ b/Mathlib/Tactic/Linter/DirectoryDependency.lean @@ -225,10 +225,6 @@ def allowedImportDirs : NamePrefixRel := .ofArray #[ (`Mathlib.Tactic.Linter, `ImportGraph), (`Mathlib.Tactic.Linter, `Mathlib.Tactic.MinImports), (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.ContextInfo), - (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Elab.Tactic.Meta), - (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Message), - (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Environment), - (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Elab.InfoTree), (`Mathlib.Tactic.Linter.TextBased, `Mathlib.Data.Nat.Notation), (`Mathlib.Tactic.Linter.UnusedInstancesInType, `Mathlib.Lean.Expr.Basic), (`Mathlib.Tactic.Linter.UnusedInstancesInType, `Mathlib.Lean.Environment), From f405742712adfc0c2c037ab54bb38b12c663b335 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Fri, 22 May 2026 22:17:49 +0100 Subject: [PATCH 132/141] chore: tweak message --- .../Tactic/Linter/OverlappingInstances.lean | 16 ++--- MathlibTest/OverlappingInstances.lean | 58 +++++++++---------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index ba4167ae496861..395ee40015eb59 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -193,22 +193,18 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option msgs := msgs.push msg if msgs.isEmpty then return none - let mut msg := ← do - if let some decl := ctx.parentDecl? then - -- Use `addMessageContextPartial` to clear the local context, - -- so as to avoid a name clash with the recursive auxiliary hypothesis of the same name. - pure m!"Overlapping instances in `{← addMessageContextPartial (.ofConstName decl)}`:\n" - else - pure m!"Overlapping instances:" + let inDecl ← if let some decl := ctx.parentDecl? then + pure m!" in `{← addMessageContextPartial (.ofConstName decl)}`" else pure "" + let mut msg := m!"Overlapping instance parameters{inDecl}:\n" for overlapMsg in msgs do msg := msg ++ m!"\n⚠️ {overlapMsg}" if needsDiamondMsg then msg := msg ++ m!"\n\n\ When a data-carrying type class has multiple potential instances coming from different \ - instances parameters, then these instances are incompatible. \ - This is an example of \"instance diamond\", which leads to unexpected unification failures.\ + instance parameters, then these potential instances are incompatible. This is an example of \ + an \"instance diamond\", which leads to unexpected unification failures.\ \n\n\ - Restructure your instance parameters to avoid this." + Delete or combine some of your instance parameters to avoid this." addMessageContextFull msg initialize registerTraceClass `overlappingInstances diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index 35bd29a3b2fd30..f1e304fbf677f4 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -28,7 +28,7 @@ error: unsolved goals inst✝¹ inst✝ : Add Nat ⊢ [Add Nat] → [Add Nat] → Bool --- -warning: Overlapping instances in `foo`: +warning: Overlapping instance parameters in `foo`: ⚠️ There are 4 `[Add Nat]` instances; one is sufficient. @@ -40,13 +40,13 @@ def foo [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := by /-- @ +3:21...+4:12 -warning: Overlapping instances in `foo₁`: +warning: Overlapping instance parameters in `foo₁`: ⚠️ `[FooBarBaz Nat]` and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. -Restructure your instance parameters to avoid this. +Delete or combine some of your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -57,7 +57,7 @@ set_option linter.overlappingInstances true in exact true /-- -warning: Overlapping instances in `foo₂`: +warning: Overlapping instance parameters in `foo₂`: ⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. ⚠️ `[FooBarBaz @@ -70,7 +70,7 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f def foo₂ [FooBarBaz Nat] [FooBarBaz Nat] [FooBarBaq Nat] : Bool := true /-- -warning: Overlapping instances in `foo₃`: +warning: Overlapping instance parameters in `foo₃`: ⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. @@ -80,7 +80,7 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f def foo₃ [FooBarBaz Nat] [FooBarBaz Nat] : Bool := true /-- -warning: Overlapping instances in `foo₄`: +warning: Overlapping instance parameters in `foo₄`: ⚠️ There are 2 `[FooBarBaz Nat]` instances; one is sufficient. ⚠️ `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. @@ -92,13 +92,13 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f theorem foo₄ [FooBarBaz Nat] [FooBarBaz Nat] [Bar Nat] : True := trivial /-- -warning: Overlapping instances in `foo₅`: +warning: Overlapping instance parameters in `foo₅`: ⚠️ `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` can be used to infer conflicting versions of `[Baz Nat]` and `[SubBar Nat]`. -When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. -Restructure your instance parameters to avoid this. +Delete or combine some of your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -106,15 +106,15 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f lemma foo₅ [FooBarBaz Nat] [FooBarBaz' Nat] : True := trivial /-- -warning: Overlapping instances in `foo₆`: +warning: Overlapping instance parameters in `foo₆`: ⚠️ `[FooBarBaz Nat]` and `[FooBarBaz' Nat]` can be used to infer conflicting versions of `[Baz Nat]`. ⚠️ `[FooBarBaz Nat]`, `[FooBarBaz' Nat]`, and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. -When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. -Restructure your instance parameters to avoid this. +Delete or combine some of your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -126,7 +126,7 @@ namespace Foo /-! Test unresolving name (`foo`, not `Foo.foo` or `_private...foo`) -/ /-- -warning: Overlapping instances in `foo`: +warning: Overlapping instance parameters in `foo`: ⚠️ There are 2 `[Add Nat]` instances; one is sufficient. @@ -146,7 +146,7 @@ class IsBar : Prop extends IsFoo class IsBaz : Prop extends IsBar /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ `[IsBar]` and `[IsBaz]` each imply `[IsFoo]`. 💡️ Of these, `[IsBar]` may be removed. @@ -162,7 +162,7 @@ class Baz1 : Type extends Baz class Baz2 : Type extends Baz /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ `[IsFoo]` and `[Bar]` each imply `[IsFoo]`. 💡️ Of these, `[IsFoo]` may be removed. @@ -177,7 +177,7 @@ example [IsBar] [Bar] : True := trivial example [Bar] [Baz] : True := trivial /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ There are 2 `[Baz]` instances; one is sufficient. @@ -187,7 +187,7 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f example [Baz] [Baz] : True := trivial /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ `[Baz]` and `[Baz1]` can be used to infer conflicting versions of `[Baz]`. 💡️ Of these, `[Baz]` may be removed. @@ -198,13 +198,13 @@ Note: This linter can be disabled with `set_option linter.overlappingInstances f example [Baz] [Baz1] : True := trivial /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ `[Baz1]` and `[Baz2]` can be used to infer conflicting versions of `[Baz]`. -When a data-carrying type class has multiple potential instances coming from different instances parameters, then these instances are incompatible. This is an example of "instance diamond", which leads to unexpected unification failures. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. -Restructure your instance parameters to avoid this. +Delete or combine some of your instance parameters to avoid this. Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ @@ -221,7 +221,7 @@ class inductive IndFoo where | mk₁ (n : Nat) | mk₂ (b : Bool) /-- -warning: Overlapping instances in `indFoo`: +warning: Overlapping instance parameters in `indFoo`: ⚠️ There are 2 `[IndFoo]` instances; one is sufficient. @@ -234,7 +234,7 @@ class inductive IndFooProp : Prop where | mk₁ (n : Nat) | mk₂ (b : Bool) /-- -warning: Overlapping instances in `indFooProp`: +warning: Overlapping instance parameters in `indFooProp`: ⚠️ There are 2 `[IndFooProp]` instances; one is sufficient. @@ -250,7 +250,7 @@ section instantiateMVars variable {α : Type*} [Repr α] /-- -warning: Overlapping instances in `needsInstantiateMVars`: +warning: Overlapping instance parameters in `needsInstantiateMVars`: ⚠️ There are 2 `[Repr α]` instances; one is sufficient. @@ -269,7 +269,7 @@ def fooNothing [Add Nat] [Add Nat] : [Add Nat] → [Add Nat] → Bool := true set_option linter.overlappingInstances false /-- -warning: Overlapping instances in `fooSomething`: +warning: Overlapping instance parameters in `fooSomething`: ⚠️ There are 4 `[Add Nat]` instances; one is sufficient. @@ -290,7 +290,7 @@ class A (α : Sort u) where class B (α : Type u) extends A α /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ `[B α]` and `[A α]` can be used to infer conflicting versions of `[A α]`. 💡️ Of these, `[A α]` may be removed. @@ -315,7 +315,7 @@ class B' (α β : Type*) [A' α] extends B α β where instance {α} : A α where /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ `[B α β]` and `[B' α β]` can be used to infer conflicting versions of `[B α β]`. 💡️ Of these, `[B α β]` may be removed. @@ -330,7 +330,7 @@ end parameters /-! Test a `where` clause. -/ /-- -warning: Overlapping instances in `List.lt'.go`: +warning: Overlapping instance parameters in `List.lt'.go`: ⚠️ There are 2 `[DecidableEq α]` instances; one is sufficient. @@ -356,7 +356,7 @@ class IsAClass2 extends IsAClass1 example [IsAClass1] [IsAClass1'] : True := trivial /-- -warning: Overlapping instances in `_example`: +warning: Overlapping instance parameters in `_example`: ⚠️ `[IsAClass1]` and `[IsAClass2]` can be used to infer conflicting versions of `[IsAClass1]`. 💡️ Of these, `[IsAClass1]` may be removed. From 6597ce8ff20f26e55833a2a4fe8b1ce8939f8bfb Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Fri, 22 May 2026 22:20:13 +0100 Subject: [PATCH 133/141] chore: directory dependency --- Mathlib/Tactic/Linter/DirectoryDependency.lean | 1 + Mathlib/Tactic/Linter/OverlappingInstances.lean | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/DirectoryDependency.lean b/Mathlib/Tactic/Linter/DirectoryDependency.lean index 2abecaaa029086..39d6bd05085bbe 100644 --- a/Mathlib/Tactic/Linter/DirectoryDependency.lean +++ b/Mathlib/Tactic/Linter/DirectoryDependency.lean @@ -225,6 +225,7 @@ def allowedImportDirs : NamePrefixRel := .ofArray #[ (`Mathlib.Tactic.Linter, `ImportGraph), (`Mathlib.Tactic.Linter, `Mathlib.Tactic.MinImports), (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.ContextInfo), + (`Mathlib.Tactic.Linter.OverlappingInstances, `Mathlib.Lean.Elab.Tactic.Meta), (`Mathlib.Tactic.Linter.TextBased, `Mathlib.Data.Nat.Notation), (`Mathlib.Tactic.Linter.UnusedInstancesInType, `Mathlib.Lean.Expr.Basic), (`Mathlib.Tactic.Linter.UnusedInstancesInType, `Mathlib.Lean.Environment), diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 395ee40015eb59..2b1ac5bf914d45 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -8,7 +8,6 @@ module public meta import Lean.Elab.Command public meta import Mathlib.Lean.ContextInfo public meta import Batteries.Lean.Position - public meta import Mathlib.Tactic.Linter.UnusedInstancesInType /-! From f4c1b858c9ad30ad1b9bb1a5a7391da130749437 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Fri, 22 May 2026 22:40:17 +0100 Subject: [PATCH 134/141] chore: more sensible unicode linter change --- Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean index 64dcc9fdb3af88..3123670cd5235a 100644 --- a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean +++ b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean @@ -181,10 +181,6 @@ public def emojis : Array Char := #[ .ofNat 0x1F389, -- 🎉️ '\u23F3', -- ⏳️ .ofNat 0x1F3C1, -- 🏁️️ -] - -/-- Unicode symbols in mathlib that may be followed by the emoji variant selector, or may not. -/ -public def maybeEmojis : Array Char := #[ '⚠' -- ⚠️ ] @@ -206,7 +202,6 @@ public def isAllowedCharacter (c : Char) : Bool := || withVSCodeAbbrev.contains c || othersInMathlib.contains c || emojis.contains c - || maybeEmojis.contains c || nonEmojis.contains c || c == UnicodeVariant.emoji || c == UnicodeVariant.text From 92708782456171a5ddfc735054c79c2c2cd735d4 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sun, 24 May 2026 01:30:35 +0100 Subject: [PATCH 135/141] adaptation --- .../MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean | 1 + 1 file changed, 1 insertion(+) diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean index 93c6d2e2923832..f737847456af61 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean @@ -438,6 +438,7 @@ theorem condExpL1CLM_of_aestronglyMeasurable' (f : α →₁[μ] F') (hfm : AESt condExpL1CLM F' hm μ f = f := condExpL1CLM_lpMeas (⟨f, hfm⟩ : lpMeas F' ℝ m 1 μ) +set_option linter.overlappingInstances false in /-- Conditional expectation of a function, in L1. Its value is 0 if the function is not integrable. The function-valued `condExp` should be used instead in most cases. -/ @[nolint unusedArguments] -- TODO: drop the completeness assumption in the definition, and fix From 888787b5989ddc77f1ae452b273baf2a462898c4 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sun, 24 May 2026 01:44:09 +0100 Subject: [PATCH 136/141] show the explanation more often --- .../Tactic/Linter/OverlappingInstances.lean | 8 +++---- MathlibTest/OverlappingInstances.lean | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 2b1ac5bf914d45..3e0e7d7981357f 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -167,8 +167,10 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option m!"There are {fvarTypes.size} `{.sbracket fvarTypes[0]!}` instances; one is sufficient." else let propOverlap ← overlaps.allM isProp + unless propOverlap do + needsDiamondMsg := true -- Ignore `Prop` overlaps when data conflicts are present. - let overlaps ← if !propOverlap then overlaps.filterM (notM <| isProp ·) else pure overlaps + let overlaps ← if propOverlap then pure overlaps else overlaps.filterM (notM <| isProp ·) let localInsts := (← getLCtx).decls.toList.reduceOption -- Otherwise, figure out which instances can be synthesized from the other instances let mut redundant := #[] @@ -184,9 +186,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let mut msg := m!"{fvarTypes} {ite propOverlap "each imply" "can be used to infer conflicting versions of"} {overlaps}." - if redundant.isEmpty then - needsDiamondMsg := true - else + unless redundant.isEmpty do let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") msg := m!"{msg}\n💡️ Of these, {redundant'} may be removed." msgs := msgs.push msg diff --git a/MathlibTest/OverlappingInstances.lean b/MathlibTest/OverlappingInstances.lean index f1e304fbf677f4..5e9019262dbb55 100644 --- a/MathlibTest/OverlappingInstances.lean +++ b/MathlibTest/OverlappingInstances.lean @@ -64,6 +64,10 @@ warning: Overlapping instance parameters in `foo₂`: Nat]`, `[FooBarBaz Nat]`, and `[FooBarBaq Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. 💡️ Of these, `[FooBarBaz Nat]` and `[FooBarBaz Nat]` may be removed. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. + +Delete or combine some of your instance parameters to avoid this. + Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in @@ -86,6 +90,10 @@ warning: Overlapping instance parameters in `foo₄`: ⚠️ `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` can be used to infer conflicting versions of `[SubBar Nat]`. 💡️ Of these, `[FooBarBaz Nat]`, `[FooBarBaz Nat]`, and `[Bar Nat]` may be removed. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. + +Delete or combine some of your instance parameters to avoid this. + Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in @@ -192,6 +200,10 @@ warning: Overlapping instance parameters in `_example`: ⚠️ `[Baz]` and `[Baz1]` can be used to infer conflicting versions of `[Baz]`. 💡️ Of these, `[Baz]` may be removed. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. + +Delete or combine some of your instance parameters to avoid this. + Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in @@ -295,6 +307,10 @@ warning: Overlapping instance parameters in `_example`: ⚠️ `[B α]` and `[A α]` can be used to infer conflicting versions of `[A α]`. 💡️ Of these, `[A α]` may be removed. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. + +Delete or combine some of your instance parameters to avoid this. + Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in @@ -320,6 +336,10 @@ warning: Overlapping instance parameters in `_example`: ⚠️ `[B α β]` and `[B' α β]` can be used to infer conflicting versions of `[B α β]`. 💡️ Of these, `[B α β]` may be removed. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. + +Delete or combine some of your instance parameters to avoid this. + Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in @@ -361,6 +381,10 @@ warning: Overlapping instance parameters in `_example`: ⚠️ `[IsAClass1]` and `[IsAClass2]` can be used to infer conflicting versions of `[IsAClass1]`. 💡️ Of these, `[IsAClass1]` may be removed. +When a data-carrying type class has multiple potential instances coming from different instance parameters, then these potential instances are incompatible. This is an example of an "instance diamond", which leads to unexpected unification failures. + +Delete or combine some of your instance parameters to avoid this. + Note: This linter can be disabled with `set_option linter.overlappingInstances false` -/ #guard_msgs in From ef0ed901fc15b1475dcc359dfaf2917b93f8c358 Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid Date: Sun, 24 May 2026 03:43:51 +0100 Subject: [PATCH 137/141] Revert "chore: more sensible unicode linter change" This reverts commit f4c1b858c9ad30ad1b9bb1a5a7391da130749437. --- Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean index 3123670cd5235a..64dcc9fdb3af88 100644 --- a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean +++ b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean @@ -181,6 +181,10 @@ public def emojis : Array Char := #[ .ofNat 0x1F389, -- 🎉️ '\u23F3', -- ⏳️ .ofNat 0x1F3C1, -- 🏁️️ +] + +/-- Unicode symbols in mathlib that may be followed by the emoji variant selector, or may not. -/ +public def maybeEmojis : Array Char := #[ '⚠' -- ⚠️ ] @@ -202,6 +206,7 @@ public def isAllowedCharacter (c : Char) : Bool := || withVSCodeAbbrev.contains c || othersInMathlib.contains c || emojis.contains c + || maybeEmojis.contains c || nonEmojis.contains c || c == UnicodeVariant.emoji || c == UnicodeVariant.text From 810ff5b80b1c4e274bd07872fb2ec99441c74e9c Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sun, 24 May 2026 20:05:04 -0400 Subject: [PATCH 138/141] Revert "chore: better unicode linter fix" This reverts commit 25632d8599aace2e7e89ccd0eb5ff623eb97cb42. --- Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean index 64dcc9fdb3af88..153d1931e91598 100644 --- a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean +++ b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean @@ -180,11 +180,7 @@ public def emojis : Array Char := #[ .ofNat 0x1F50D, -- 🔍️ .ofNat 0x1F389, -- 🎉️ '\u23F3', -- ⏳️ - .ofNat 0x1F3C1, -- 🏁️️ -] - -/-- Unicode symbols in mathlib that may be followed by the emoji variant selector, or may not. -/ -public def maybeEmojis : Array Char := #[ + .ofNat 0x1F3C1, -- 🏁️ '⚠' -- ⚠️ ] @@ -206,7 +202,6 @@ public def isAllowedCharacter (c : Char) : Bool := || withVSCodeAbbrev.contains c || othersInMathlib.contains c || emojis.contains c - || maybeEmojis.contains c || nonEmojis.contains c || c == UnicodeVariant.emoji || c == UnicodeVariant.text From e10fd747fc3ad3458a6897fc51988dbceba08c0f Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sun, 24 May 2026 20:06:00 -0400 Subject: [PATCH 139/141] =?UTF-8?q?Revert=20"chore:=20update=20unicode=20l?= =?UTF-8?q?inter=20to=20allow=20=E2=9A=A0=EF=B8=8F"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a877b3f6dc2ba6900430e8911d2be2398bc027bd. --- Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean index 153d1931e91598..b4b7a8b9e8c9e3 100644 --- a/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean +++ b/Mathlib/Tactic/Linter/TextBased/UnicodeLinter.lean @@ -180,8 +180,7 @@ public def emojis : Array Char := #[ .ofNat 0x1F50D, -- 🔍️ .ofNat 0x1F389, -- 🎉️ '\u23F3', -- ⏳️ - .ofNat 0x1F3C1, -- 🏁️ - '⚠' -- ⚠️ + .ofNat 0x1F3C1 -- 🏁️ ] /-- Unicode symbols in mathlib that should always be followed by the text variant selector. -/ From 17fe8ddc908432b891cd4a567366378396700cbe Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sun, 24 May 2026 21:37:34 -0400 Subject: [PATCH 140/141] chore: `ite` -> `if then else` since it no longer saves us a line --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 3e0e7d7981357f..7c4d6adf3ca4e5 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -184,7 +184,7 @@ def runLinter (ctx : ContextInfo) (lctx : LocalContext) (expectedType? : Option let fvarTypes := .andList <| fvarTypes.toList.map (m!"`{.sbracket ·}`") let overlaps := .andList <| overlaps.toList.map (m!"`{.sbracket ·}`") let mut msg := - m!"{fvarTypes} {ite propOverlap "each imply" + m!"{fvarTypes} {if propOverlap then "each imply" else "can be used to infer conflicting versions of"} {overlaps}." unless redundant.isEmpty do let redundant' := .andList <| redundant.toList.map (m!"`{.sbracket ·}`") From d7702a0faac7c282c88eeab2afaf2e9623712521 Mon Sep 17 00:00:00 2001 From: thorimur <68410468+thorimur@users.noreply.github.com> Date: Sun, 24 May 2026 21:40:42 -0400 Subject: [PATCH 141/141] chore: slightly nicer trace message --- Mathlib/Tactic/Linter/OverlappingInstances.lean | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Linter/OverlappingInstances.lean b/Mathlib/Tactic/Linter/OverlappingInstances.lean index 7c4d6adf3ca4e5..4bfd16a8ee01df 100644 --- a/Mathlib/Tactic/Linter/OverlappingInstances.lean +++ b/Mathlib/Tactic/Linter/OverlappingInstances.lean @@ -221,7 +221,8 @@ def overlappingInstances : Linter where for t in ← getInfoTrees do for (ref, ctx, info) in t.getDeclBodyInfos do let some (lctx, expectedType?) := info.getLCtx? | pure () - withTraceNode `overlappingInstances (fun _ ↦ return m!"linting `{ctx.parentDecl?}`") do + withTraceNode `overlappingInstances + (fun _ ↦ return m!"linting `{.ofConstName <| ctx.parentDecl?.getD .anonymous}`") do let some msg ← runLinter ctx lctx expectedType? | pure () /- Log the warning from the declaration's selection range (usually the declaration name, or `instance`) to the body if possible. This underlines the hypotheses and type,