Skip to content

Commit d33aece

Browse files
authored
feat: list definitions in defeq problems that could not be unfolded for lack of @[expose] (#10158)
This PR adds information about definitions blocked from unfolding via the module system to type defeq errors.
1 parent 9a7bab5 commit d33aece

File tree

7 files changed

+67
-13
lines changed

7 files changed

+67
-13
lines changed

src/Lean/Meta/Basic.lean

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,10 @@ structure PostponedEntry where
415415
structure Diagnostics where
416416
/-- Number of times each declaration has been unfolded -/
417417
unfoldCounter : PHashMap Name Nat := {}
418+
/--
419+
Number of times each axiom was tried to be unfolded, which may point to an inaccessible def value.
420+
-/
421+
unfoldAxiomCounter : PHashMap Name Nat := {}
418422
/-- Number of times `f a =?= f b` heuristic has been used per function `f`. -/
419423
heuristicCounter : PHashMap Name Nat := {}
420424
/-- Number of times a TC instance is used. -/
@@ -670,30 +674,39 @@ def mkInfoCacheKey (expr : Expr) (nargs? : Option Nat) : MetaM InfoCacheKey :=
670674

671675
/-- If diagnostics are enabled, record that `declName` has been unfolded. -/
672676
def recordUnfold (declName : Name) : MetaM Unit := do
673-
modifyDiag fun { unfoldCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
677+
modifyDiag fun { unfoldCounter, unfoldAxiomCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
674678
let newC := if let some c := unfoldCounter.find? declName then c + 1 else 1
675-
{ unfoldCounter := unfoldCounter.insert declName newC, heuristicCounter, instanceCounter, synthPendingFailures }
679+
{ unfoldCounter := unfoldCounter.insert declName newC, unfoldAxiomCounter, heuristicCounter, instanceCounter, synthPendingFailures }
680+
681+
def recordUnfoldAxiom (declName : Name) : MetaM Unit := do
682+
modifyDiag fun { unfoldCounter, unfoldAxiomCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
683+
let newC := if let some c := unfoldAxiomCounter.find? declName then c + 1 else 1
684+
{ unfoldCounter, unfoldAxiomCounter := unfoldAxiomCounter.insert declName newC, heuristicCounter, instanceCounter, synthPendingFailures }
685+
686+
def resetUnfoldAxiom : MetaM Unit := do
687+
modifyDiag fun { unfoldCounter, heuristicCounter, instanceCounter, synthPendingFailures, .. } =>
688+
{ unfoldCounter, unfoldAxiomCounter := {}, heuristicCounter, instanceCounter, synthPendingFailures }
676689

677690
/-- If diagnostics are enabled, record that heuristic for solving `f a =?= f b` has been used. -/
678691
def recordDefEqHeuristic (declName : Name) : MetaM Unit := do
679-
modifyDiag fun { unfoldCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
692+
modifyDiag fun { unfoldCounter, unfoldAxiomCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
680693
let newC := if let some c := heuristicCounter.find? declName then c + 1 else 1
681-
{ unfoldCounter, heuristicCounter := heuristicCounter.insert declName newC, instanceCounter, synthPendingFailures }
694+
{ unfoldCounter, unfoldAxiomCounter, heuristicCounter := heuristicCounter.insert declName newC, instanceCounter, synthPendingFailures }
682695

683696
/-- If diagnostics are enabled, record that instance `declName` was used during TC resolution. -/
684697
def recordInstance (declName : Name) : MetaM Unit := do
685-
modifyDiag fun { unfoldCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
698+
modifyDiag fun { unfoldCounter, unfoldAxiomCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
686699
let newC := if let some c := instanceCounter.find? declName then c + 1 else 1
687-
{ unfoldCounter, heuristicCounter, instanceCounter := instanceCounter.insert declName newC, synthPendingFailures }
700+
{ unfoldCounter, unfoldAxiomCounter, heuristicCounter, instanceCounter := instanceCounter.insert declName newC, synthPendingFailures }
688701

689702
/-- If diagnostics are enabled, record that synth pending failures. -/
690703
def recordSynthPendingFailure (type : Expr) : MetaM Unit := do
691704
if (← isDiagnosticsEnabled) then
692705
unless (← get).diag.synthPendingFailures.contains type do
693706
-- We need to save the full context since type class resolution uses multiple metavar contexts and different local contexts
694707
let msg ← addMessageContextFull m!"{type}"
695-
modifyDiag fun { unfoldCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
696-
{ unfoldCounter, heuristicCounter, instanceCounter, synthPendingFailures := synthPendingFailures.insert type msg }
708+
modifyDiag fun { unfoldCounter, unfoldAxiomCounter, heuristicCounter, instanceCounter, synthPendingFailures } =>
709+
{ unfoldCounter, unfoldAxiomCounter, heuristicCounter, instanceCounter, synthPendingFailures := synthPendingFailures.insert type msg }
697710

698711
def getLocalInstances : MetaM LocalInstances :=
699712
return (← read).localInstances

src/Lean/Meta/Check.lean

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module
88
prelude
99
public import Lean.Meta.InferType
1010
public import Lean.Meta.Sorry
11+
import Lean.AddDecl
1112

1213
public section
1314

@@ -206,26 +207,39 @@ def mkHasTypeButIsExpectedMsg (givenType expectedType : Expr)
206207
(trailing? : Option MessageData := none) (trailingExprs : Array Expr := #[])
207208
: MetaM MessageData := do
208209
return MessageData.ofLazyM (es := #[givenType, expectedType] ++ trailingExprs) do
209-
try
210+
let mut msg ← (try
210211
let givenTypeType ← inferType givenType
211212
let expectedTypeType ← inferType expectedType
212213
if (← isDefEqGuarded givenTypeType expectedTypeType) then
213214
let (givenType, expectedType) ← addPPExplicitToExposeDiff givenType expectedType
214215
let trailing := trailing?.map (m!"\n" ++ ·) |>.getD .nil
215-
return m!"has type{indentExpr givenType}\n\
216+
pure m!"has type{indentExpr givenType}\n\
216217
but is expected to have type{indentExpr expectedType}{trailing}"
217218
else
218219
let (givenType, expectedType) ← addPPExplicitToExposeDiff givenType expectedType
219220
let (givenTypeType, expectedTypeType) ← addPPExplicitToExposeDiff givenTypeType expectedTypeType
220221
let trailing := match trailing? with
221222
| none => inlineExprTrailing expectedTypeType
222223
| some trailing => inlineExpr expectedTypeType ++ trailing
223-
return m!"has type{indentExpr givenType}\nof sort{inlineExpr givenTypeType}\
224+
pure m!"has type{indentExpr givenType}\nof sort{inlineExpr givenTypeType}\
224225
but is expected to have type{indentExpr expectedType}\nof sort{trailing}"
225226
catch _ =>
226227
let (givenType, expectedType) ← addPPExplicitToExposeDiff givenType expectedType
227228
let trailing := trailing?.map (m!"\n" ++ ·) |>.getD .nil
228-
return m!"has type{indentExpr givenType}\nbut is expected to have type{indentExpr expectedType}{trailing}"
229+
pure m!"has type{indentExpr givenType}\nbut is expected to have type{indentExpr expectedType}{trailing}")
230+
let env ← getEnv
231+
if env.header.isModule then
232+
let origDiag := (← get).diag
233+
let _ ← observing <| withOptions (diagnostics.set · true) <| isDefEq givenType expectedType
234+
let blocked := (← get).diag.unfoldAxiomCounter.toList.filterMap fun (n, count) => do
235+
let count := count - origDiag.unfoldAxiomCounter.findD n 0
236+
guard <| count > 0 && getOriginalConstKind? env n matches some .defn
237+
return m!"{.ofConstName n} ↦ {count}"
238+
if !blocked.isEmpty then
239+
msg := msg ++ MessageData.note m!"The following definitions were not unfolded because \
240+
their definition is not exposed:{indentD <| .joinSep blocked Format.line}"
241+
modify ({ · with diag := origDiag })
242+
return msg
229243

230244
def throwAppTypeMismatch (f a : Expr) : MetaM α := do
231245
-- Clarify that `a` is "last" only if it may be confused with some preceding argument; otherwise,

src/Lean/Meta/GetUnfoldableConst.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ def getUnfoldableConstNoEx? (constName : Name) : MetaM (Option ConstantInfo) :=
5858
match (← getEnv).find? constName with
5959
| some (info@(.thmInfo _)) => getTheoremInfo info
6060
| some (info@(.defnInfo _)) => if (← canUnfold info) then return info else return none
61+
| some (.axiomInfo _) => recordUnfoldAxiom constName; return none
6162
| _ => return none
6263

6364
end Meta

src/Lean/Meta/WHNF.lean

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,7 @@ where
691691
deltaBetaDefinition c lvls e.getAppRevArgs (fun _ => return e) go
692692
else
693693
return e
694+
| .axiomInfo val => recordUnfoldAxiom val.name; return e
694695
| _ => return e
695696
| .proj _ i c =>
696697
let k (c : Expr) := do
@@ -813,6 +814,8 @@ mutual
813814
recordUnfold fInfo.name
814815
deltaBetaDefinition fInfo fLvls e.getAppRevArgs (fun _ => pure none) (fun e => pure (some e))
815816
else
817+
if fInfo.isAxiom then
818+
recordUnfoldAxiom fInfo.name
816819
return none
817820
if smartUnfolding.get (← getOptions) then
818821
match ((← getEnv).find? (skipRealize := true) (mkSmartUnfoldingNameFor fInfo.name)) with
@@ -882,7 +885,10 @@ mutual
882885
if smartUnfolding.get (← getOptions) && (← getEnv).contains (mkSmartUnfoldingNameFor declName) then
883886
return none
884887
else
885-
unless cinfo.hasValue do return none
888+
unless cinfo.hasValue do
889+
if cinfo.isAxiom then
890+
recordUnfoldAxiom cinfo.name
891+
return none
886892
deltaDefinition cinfo lvls
887893
(fun _ => pure none)
888894
(fun e => do recordUnfold declName; pure (some e))

tests/pkg/module/Module/Basic.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ has type
9696
Vector Unit 1
9797
but is expected to have type
9898
Vector Unit f
99+
100+
Note: The following definitions were not unfolded because their definition is not exposed:
101+
f ↦ 1
99102
-/
100103
#guard_msgs in
101104
public theorem v (x : Vector Unit f) (y : Vector Unit 1) : x = y := testSorry

tests/pkg/module/Module/Imported.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@ info: def f : Nat :=
1212
#guard_msgs in
1313
#print f
1414
15+
/--
16+
error: Type mismatch
17+
rfl
18+
has type
19+
?m.5 = ?m.5
20+
but is expected to have type
21+
f = 1
22+
23+
Note: The following definitions were not unfolded because their definition is not exposed:
24+
f ↦ 1
25+
-/
26+
#guard_msgs in
27+
example : f = 1 := rfl
28+
1529
/-! Theorems should be exported without their bodies -/
1630
1731
/--

tests/pkg/module/Module/ImportedAll.lean

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ has type
2424
Vector Unit 1
2525
but is expected to have type
2626
Vector Unit f
27+
28+
Note: The following definitions were not unfolded because their definition is not exposed:
29+
f ↦ 1
2730
-/
2831
#guard_msgs in
2932
public theorem v (x : Vector Unit f) (y : Vector Unit 1) : x = y := sorry

0 commit comments

Comments
 (0)