@@ -856,11 +856,24 @@ private def applyCachedAbstractResult? (type : Expr) (abstResult? : Option Abstr
856856 applyAbstractResult? type abstResult?
857857
858858/-- Helper function for caching synthesized type class instances. -/
859- private def cacheResult (cacheKey : SynthInstanceCacheKey) (relSynthPendingDepth : Option Nat)
860- (kind : PreprocessKind) (abstResult? : Option AbstractMVarsResult) (result? : Option Expr) : MetaM Unit := do
859+ private def cacheResult (cacheKey : SynthInstanceCacheKey) (synthPendingDepth : Nat)
860+ (activity : SynthPendingActivity) (kind : PreprocessKind)
861+ (abstResult? : Option AbstractMVarsResult) (result? : Option Expr) : MetaM Unit := do
861862 let insert (result : Option AbstractMVarsResult) : MetaM Unit :=
862- modify fun s => { s with
863- cache.synthInstance := s.cache.synthInstance.insert cacheKey { relSynthPendingDepth, result } }
863+ modify fun s => { s with cache.synthInstance :=
864+ -- Merge into the existing entry (if any) so that shared and depth-exact results for
865+ -- the same key can coexist. Re-reading the entry here is required for correctness:
866+ -- nested queries during the search may have stored results for the same key.
867+ let entry := s.cache.synthInstance.find? cacheKey |>.getD {}
868+ let entry :=
869+ if activity.guardHit then
870+ -- The synthesis hit the `maxSynthPendingDepth` give-up, so the result is only
871+ -- valid at the exact depth; all others are shared, bounded by their relative
872+ -- activity depth.
873+ { entry with exact? := some (synthPendingDepth, result) }
874+ else
875+ { entry with shared? := some (activity.maxDepth.map (· - synthPendingDepth), result) }
876+ s.cache.synthInstance.insert cacheKey entry }
864877 match abstResult? with
865878 | none => insert none
866879 | some abstResult =>
@@ -874,6 +887,10 @@ private def cacheResult (cacheKey : SynthInstanceCacheKey) (relSynthPendingDepth
874887 else
875888 insert (some abstResult)
876889
890+ /-- Trace-message suffix showing a nonzero `synthPendingDepth`. -/
891+ private def depthSuffix (synthPendingDepth : Nat) : MessageData :=
892+ if synthPendingDepth == 0 then m! "" else m! " (synthPendingDepth := { synthPendingDepth} )"
893+
877894def synthInstanceCore? (type : Expr) (maxResultSize? : Option Nat := none) : MetaM (Option Expr) := do
878895 let opts ← getOptions
879896 let maxResultSize := maxResultSize?.getD (synthInstance.maxSize.get opts)
@@ -886,8 +903,6 @@ def synthInstanceCore? (type : Expr) (maxResultSize? : Option Nat := none) : Met
886903 let type ← instantiateMVars type
887904 let { type, cacheKeyType, kind } ← preprocess type
888905 let synthPendingDepth := (← read).synthPendingDepth
889- let depthSuffix : MessageData :=
890- if synthPendingDepth == 0 then m! "" else m! " (synthPendingDepth := { synthPendingDepth} )"
891906 -- Fold `synthPending` activity into the enclosing query's accumulator, if any.
892907 let foldActivity (activity : SynthPendingActivity) : MetaM Unit := do
893908 if activity.maxDepth.isSome || activity.guardHit then
@@ -898,71 +913,64 @@ def synthInstanceCore? (type : Expr) (maxResultSize? : Option Nat := none) : Met
898913 | some d, none | none, some d => some d
899914 | none, none => none
900915 guardHit := a.guardHit || activity.guardHit }
901- let applyCached (entry : SynthInstanceCacheEntry ) : MetaM (Option Expr) := do
902- trace[Meta.synthInstance.cache] "cached{ depthSuffix} : { type} "
903- let result? ← applyCachedAbstractResult? type entry.result
916+ let applyCached (abstResult? : Option AbstractMVarsResult ) : MetaM (Option Expr) := do
917+ trace[Meta.synthInstance.cache] "cached{ depthSuffix synthPendingDepth } : { type} "
918+ let result? ← applyCachedAbstractResult? type abstResult?
904919 trace[Meta.synthInstance] "result { result?} (cached)"
905920 return result?
906- let sharedKey : SynthInstanceCacheKey := { localInsts, type := cacheKeyType, synthPendingDepth := none }
907- let depthKey := { sharedKey with synthPendingDepth := some synthPendingDepth }
908- let maxSynthPending := maxSynthPendingDepth.get (← getOptions)
909- let sharedEntry? :=
910- match (← get).cache.synthInstance.find? sharedKey with
911- | some entry =>
912- match entry.relSynthPendingDepth with
913- | none => some entry
914- | some rel =>
915- -- Valid iff no `synthPending` invocation can reach the give-up threshold at the
916- -- current depth; see `SynthInstanceCacheEntry.relSynthPendingDepth`.
917- if synthPendingDepth + rel ≤ maxSynthPending then some entry else none
918- | none => none
919- if let some entry := sharedEntry? then
920- -- Reusing the entry re-enacts its `synthPending` decisions at the current depth.
921- foldActivity { maxDepth := entry.relSynthPendingDepth.map (synthPendingDepth + ·) }
922- applyCached entry
923- else if let some entry := (← get).cache.synthInstance.find? depthKey then
924- -- The entry's synthesis hit the `maxSynthPendingDepth` give-up, so reusing it keeps
925- -- the enclosing query depth-exact as well.
926- foldActivity { maxDepth := some synthPendingDepth, guardHit := true }
927- applyCached entry
928- else
929- trace[Meta.synthInstance.cache] "new{ depthSuffix} : { type} "
930- let activityRef ← IO.mkRef {}
931- let abstResult? ← withReader (fun ctx => { ctx with synthPendingActivityRef? := some activityRef }) do
932- withNewMCtxDepth (allowLevelAssignments := true ) do
933- match kind with
934- | .noMVars =>
935- /-
936- **Note** : The expensive `preprocessOutParam` step is morally **not** needed here because
937- the output params should be uniquely determined by the input params. During type class
938- resolution, definitional equality only unfolds `[reducible]` and `[instance_reducible]`
939- declarations. This is a contract with our users to ensure performance is reasonable.
940- However, the same `OrderDual` declaration that creates problems for `assignOutParams`
941- also prevents us from using this optimization. As an example, suppose we are trying to
942- synthesize
943- ```
944- FunLike F (OrderDual α) (OrderDual β)
945- ```
946- where the last two arguments of `FunLike` are output parameters. This term has no
947- metavariables, and it seems natural to skip `preprocessOutParam`, which would replace
948- the last two arguments with metavariables. However, if we don't replace them,
949- TC resolution fails because it cannot unfold `OrderDual` since it is semireducible.
950-
951- **Note** : We should remove `preprocessOutParam` from the following line as soon as
952- Mathlib refactors `OrderDual`.
953- -/
954- SynthInstance.main (← preprocessOutParam type) maxResultSize
955- | .mvarsNoOutputParams => SynthInstance.main type maxResultSize
956- | .mvarsOutputParams => SynthInstance.main (← preprocessOutParam type) maxResultSize
957- let result? ← applyAbstractResult? type abstResult?
958- trace[Meta.synthInstance] "result { result?} "
959- let activity ← activityRef.get
960- foldActivity activity
961- -- Results whose synthesis hit the `maxSynthPendingDepth` give-up are only valid at
962- -- the exact depth; all others are shared, bounded by their relative activity depth.
963- let key := if activity.guardHit then depthKey else sharedKey
964- cacheResult key (activity.maxDepth.map (· - synthPendingDepth)) kind abstResult? result?
965- return result?
921+ let key : SynthInstanceCacheKey := { localInsts, type := cacheKeyType }
922+ if let some entry := (← get).cache.synthInstance.find? key then
923+ if let some (rel, abstResult?) := entry.shared? then
924+ -- Valid iff no `synthPending` invocation can reach the give-up threshold at the
925+ -- current depth; see `SynthInstanceCacheEntry.shared?`.
926+ let valid ←
927+ match rel with
928+ | none => pure true
929+ | some rel => do pure (synthPendingDepth + rel ≤ maxSynthPendingDepth.get (← getOptions))
930+ if valid then
931+ -- Reusing the result re-enacts its `synthPending` decisions at the current depth.
932+ foldActivity { maxDepth := match rel with | none => none | some rel => some (synthPendingDepth + rel) }
933+ return (← applyCached abstResult?)
934+ if let some (depth, abstResult?) := entry.exact? then
935+ if depth == synthPendingDepth then
936+ -- The result's synthesis hit the `maxSynthPendingDepth` give-up, so reusing it
937+ -- keeps the enclosing query depth-exact as well.
938+ foldActivity { maxDepth := some synthPendingDepth, guardHit := true }
939+ return (← applyCached abstResult?)
940+ trace[Meta.synthInstance.cache] "new{ depthSuffix synthPendingDepth} : { type} "
941+ let activityRef ← IO.mkRef {}
942+ let abstResult? ← withReader (fun ctx => { ctx with synthPendingActivityRef? := some activityRef }) do
943+ withNewMCtxDepth (allowLevelAssignments := true ) do
944+ match kind with
945+ | .noMVars =>
946+ /-
947+ **Note** : The expensive `preprocessOutParam` step is morally **not** needed here because
948+ the output params should be uniquely determined by the input params. During type class
949+ resolution, definitional equality only unfolds `[reducible]` and `[instance_reducible]`
950+ declarations. This is a contract with our users to ensure performance is reasonable.
951+ However, the same `OrderDual` declaration that creates problems for `assignOutParams`
952+ also prevents us from using this optimization. As an example, suppose we are trying to
953+ synthesize
954+ ```
955+ FunLike F (OrderDual α) (OrderDual β)
956+ ```
957+ where the last two arguments of `FunLike` are output parameters. This term has no
958+ metavariables, and it seems natural to skip `preprocessOutParam`, which would replace
959+ the last two arguments with metavariables. However, if we don't replace them,
960+ TC resolution fails because it cannot unfold `OrderDual` since it is semireducible.
961+
962+ **Note** : We should remove `preprocessOutParam` from the following line as soon as
963+ Mathlib refactors `OrderDual`.
964+ -/
965+ SynthInstance.main (← preprocessOutParam type) maxResultSize
966+ | .mvarsNoOutputParams => SynthInstance.main type maxResultSize
967+ | .mvarsOutputParams => SynthInstance.main (← preprocessOutParam type) maxResultSize
968+ let result? ← applyAbstractResult? type abstResult?
969+ trace[Meta.synthInstance] "result { result?} "
970+ let activity ← activityRef.get
971+ foldActivity activity
972+ cacheResult key synthPendingDepth activity kind abstResult? result?
973+ return result?
966974
967975def synthInstance? (type : Expr) (maxResultSize? : Option Nat := none) : MetaM (Option Expr) := do profileitM Exception "typeclass inference" (← getOptions) (decl := type.getAppFn.constName?.getD .anonymous) do
968976 synthInstanceCore? type maxResultSize?
0 commit comments