Skip to content

Commit b078f6d

Browse files
authored
Cache ResultCaps (#23198)
This fixes problems encountered for #15923 where we got infinite recursions caused by infinite streams of new ResultCap instances that were added to capture sets. The analysis of the problem showed that there is a cycle of dependencies involving both forwards and backwards propagations of ResultCap instances between BiMapped capture sets linked by SubstBindingMaps. Each propagation would create a new derived ResultCap instance. We could try to solve the problem by having SubstBindingMaps remember their mappings and have their inverses work backwards. But that could still produce an infinite stream if there was a cycle of SubstBindingMaps of length > 2. So we fix the problem at the root by allowing only one derived ResultCap instance per original ResultCap / binder pair. Fixes #15923 Based on #23184. Only last commit is new.
2 parents 3a2635d + b27874c commit b078f6d

38 files changed

+1532
-1433
lines changed

compiler/src/dotty/tools/dotc/cc/CCState.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class CCState:
118118

119119
private var capIsRoot: Boolean = false
120120

121-
private var ignoreFreshLevels: Boolean = false
121+
private var collapseFresh: Boolean = false
122122

123123
object CCState:
124124

@@ -164,20 +164,21 @@ object CCState:
164164
/** Is `caps.cap` a root capability that is allowed to subsume other capabilities? */
165165
def capIsRoot(using Context): Boolean = ccState.capIsRoot
166166

167-
/** Run `op` under the assumption that all root.Fresh instances are equal.
167+
/** Run `op` under the assumption that all FreshCap instances are equal
168+
* to each other and to GlobalCap.
168169
* Needed to make override checking of types containing fresh work.
169170
* Asserted in override checking, tested in maxSubsumes.
170171
* Is this sound? Test case is neg-custom-args/captures/leaked-curried.scala.
171172
*/
172-
inline def ignoringFreshLevels[T](op: => T)(using Context): T =
173+
inline def withCollapsedFresh[T](op: => T)(using Context): T =
173174
if isCaptureCheckingOrSetup then
174175
val ccs = ccState
175-
val saved = ccs.ignoreFreshLevels
176-
ccs.ignoreFreshLevels = true
177-
try op finally ccs.ignoreFreshLevels = saved
176+
val saved = ccs.collapseFresh
177+
ccs.collapseFresh = true
178+
try op finally ccs.collapseFresh = saved
178179
else op
179180

180-
/** Should all root.Fresh instances be treated equal? */
181-
def ignoreFreshLevels(using Context): Boolean = ccState.ignoreFreshLevels
181+
/** Should all FreshCap instances be treated as equal to GlobalCap? */
182+
def collapseFresh(using Context): Boolean = ccState.collapseFresh
182183

183184
end CCState

0 commit comments

Comments
 (0)