@@ -106,51 +106,132 @@ object Types extends TypeUtils {
106
106
// nextId
107
107
// }
108
108
109
- /** A cache indicating whether the type was still provisional, last time we checked */
110
- @ sharable private var mightBeProvisional = true
109
+ type ProvisionalState = util.HashMap [Type , Type ]
111
110
112
- /** Is this type still provisional? This is the case if the type contains, or depends on,
113
- * uninstantiated type variables or type symbols that have the Provisional flag set.
114
- * This is an antimonotonic property - once a type is not provisional, it stays so forever.
115
- *
116
- * FIXME: The semantics of this flag are broken by the existence of `TypeVar#resetInst`,
117
- * a non-provisional type could go back to being provisional after
118
- * a call to `resetInst`. This means all caches that rely on `isProvisional`
119
- * can likely end up returning stale results.
120
- */
121
- def isProvisional (using Context ): Boolean = mightBeProvisional && testProvisional
122
-
123
- private def testProvisional (using Context ): Boolean =
111
+ def currentProvisionalState (using Context ): ProvisionalState =
112
+ val state : ProvisionalState = util.HashMap ()
113
+ // Compared to `testProvisional`, we don't use short-circuiting or,
114
+ // because we want to collect all provisional types.
124
115
class ProAcc extends TypeAccumulator [Boolean ]:
125
- override def apply (x : Boolean , t : Type ) = x || test(t, this )
116
+ override def apply (x : Boolean , t : Type ) = x | test(t, this )
126
117
def test (t : Type , theAcc : TypeAccumulator [Boolean ] | Null ): Boolean =
127
118
if t.mightBeProvisional then
128
119
t.mightBeProvisional = t match
129
120
case t : TypeRef =>
130
- t.currentSymbol.isProvisional || ! t.currentSymbol.isStatic && {
121
+ if t.currentSymbol.isProvisional then
122
+ // When t is a TypeRef and its symbol is provisional,
123
+ // t will be considered provisional and its state is always updating.
124
+ state(t) = t
125
+ true
126
+ else if ! t.currentSymbol.isStatic then
131
127
(t : Type ).mightBeProvisional = false // break cycles
132
- test(t.prefix, theAcc)
133
- || t.denot.infoOrCompleter.match
134
- case info : LazyType => true
135
- case info : AliasingBounds => test(info.alias, theAcc)
136
- case TypeBounds (lo, hi) => test(lo, theAcc) || test(hi, theAcc)
137
- case _ => false
138
- }
128
+ if test(t.prefix, theAcc) then
129
+ // If the prefix is provisional, some provisional type from it
130
+ // must have been added to state, so we don't need to add t.
131
+ true
132
+ else t.denot.infoOrCompleter.match
133
+ case info : LazyType =>
134
+ state(t) = info
135
+ true
136
+ case info : AliasingBounds =>
137
+ test(info.alias, theAcc)
138
+ case TypeBounds (lo, hi) =>
139
+ test(lo, theAcc) | test(hi, theAcc)
140
+ case _ =>
141
+ // If a TypeRef has been fully completed, it is no longer provisional,
142
+ // so we don't need to traverse its info.
143
+ false
144
+ else false
139
145
case t : TermRef =>
140
146
! t.currentSymbol.isStatic && test(t.prefix, theAcc)
141
147
case t : AppliedType =>
142
- t.fold(false , (x, tp) => x || test(tp, theAcc))
148
+ t.fold(false , (x, tp) => x | test(tp, theAcc))
143
149
case t : TypeVar =>
144
- ! t.isPermanentlyInstantiated || test(t.permanentInst, theAcc)
150
+ if t.isPermanentlyInstantiated then
151
+ test(t.permanentInst, theAcc)
152
+ else
153
+ val inst = t.instanceOpt
154
+ if inst.exists then
155
+ // We want to store the temporary instance to the state
156
+ // in order to reuse the cache when possible.
157
+ state(t) = inst
158
+ test(inst, theAcc)
159
+ else state(t) = t
160
+ true
145
161
case t : LazyRef =>
146
- ! t.completed || test(t.ref, theAcc)
162
+ if ! t.completed then
163
+ state(t) = t
164
+ true
165
+ else
166
+ test(t.ref, theAcc)
147
167
case _ =>
148
168
(if theAcc != null then theAcc else ProAcc ()).foldOver(false , t)
149
169
end if
150
170
t.mightBeProvisional
151
171
end test
152
172
test(this , null )
153
- end testProvisional
173
+ state
174
+ end currentProvisionalState
175
+
176
+ def isCacheUpToDate (
177
+ currentState : ProvisionalState ,
178
+ lastState : ProvisionalState | Null )
179
+ (using Context ): Boolean =
180
+ lastState != null
181
+ && currentState.size == lastState.size
182
+ && currentState.iterator.forall: (tp, info) =>
183
+ lastState.contains(tp) && {
184
+ tp match
185
+ case tp : TypeRef => (info ne tp) && (info eq lastState(tp))
186
+ case _=> info eq lastState(tp)
187
+ }
188
+
189
+ /** A cache indicating whether the type was still provisional, last time we checked */
190
+ @ sharable private var mightBeProvisional = true
191
+
192
+ /** Is this type still provisional? This is the case if the type contains, or depends on,
193
+ * uninstantiated type variables or type symbols that have the Provisional flag set.
194
+ * This is an antimonotonic property - once a type is not provisional, it stays so forever.
195
+ *
196
+ * FIXME: The semantics of this flag are broken by the existence of `TypeVar#resetInst`,
197
+ * a non-provisional type could go back to being provisional after
198
+ * a call to `resetInst`. This means all caches that rely on `isProvisional`
199
+ * can likely end up returning stale results.
200
+ */
201
+ // def isProvisional(using Context): Boolean = mightBeProvisional && testProvisional
202
+ def isProvisional (using Context ): Boolean = mightBeProvisional && ! currentProvisionalState.isEmpty
203
+
204
+ // private def testProvisional(using Context): Boolean =
205
+ // class ProAcc extends TypeAccumulator[Boolean]:
206
+ // override def apply(x: Boolean, t: Type) = x || test(t, this)
207
+ // def test(t: Type, theAcc: TypeAccumulator[Boolean] | Null): Boolean =
208
+ // if t.mightBeProvisional then
209
+ // t.mightBeProvisional = t match
210
+ // case t: TypeRef =>
211
+ // t.currentSymbol.isProvisional || !t.currentSymbol.isStatic && {
212
+ // (t: Type).mightBeProvisional = false // break cycles
213
+ // test(t.prefix, theAcc)
214
+ // || t.denot.infoOrCompleter.match
215
+ // case info: LazyType => true
216
+ // case info: AliasingBounds => test(info.alias, theAcc)
217
+ // case TypeBounds(lo, hi) => test(lo, theAcc) || test(hi, theAcc)
218
+ // case _ => false
219
+ // }
220
+ // case t: TermRef =>
221
+ // !t.currentSymbol.isStatic && test(t.prefix, theAcc)
222
+ // case t: AppliedType =>
223
+ // t.fold(false, (x, tp) => x || test(tp, theAcc))
224
+ // case t: TypeVar =>
225
+ // !t.isPermanentlyInstantiated || test(t.permanentInst, theAcc)
226
+ // case t: LazyRef =>
227
+ // !t.completed || test(t.ref, theAcc)
228
+ // case _ =>
229
+ // (if theAcc != null then theAcc else ProAcc()).foldOver(false, t)
230
+ // end if
231
+ // t.mightBeProvisional
232
+ // end test
233
+ // test(this, null)
234
+ // end testProvisional
154
235
155
236
/** Is this type different from NoType? */
156
237
final def exists : Boolean = this .ne(NoType )
@@ -2306,10 +2387,12 @@ object Types extends TypeUtils {
2306
2387
2307
2388
private var myName : Name | Null = null
2308
2389
private var lastDenotation : Denotation | Null = null
2390
+ private var lastDenotationProvState : ProvisionalState | Null = null
2309
2391
private var lastSymbol : Symbol | Null = null
2310
2392
private var checkedPeriod : Period = Nowhere
2311
2393
private var myStableHash : Byte = 0
2312
2394
private var mySignature : Signature = uninitialized
2395
+ private var mySignatureProvState : ProvisionalState | Null = null
2313
2396
private var mySignatureRunId : Int = NoRunId
2314
2397
2315
2398
// Invariants:
@@ -2344,9 +2427,11 @@ object Types extends TypeUtils {
2344
2427
else if ctx.erasedTypes then atPhase(erasurePhase)(computeSignature)
2345
2428
else symbol.asSeenFrom(prefix).signature
2346
2429
2347
- if ctx.runId != mySignatureRunId then
2430
+ if ctx.runId != mySignatureRunId
2431
+ || ! isCacheUpToDate(currentProvisionalState, mySignatureProvState) then
2348
2432
mySignature = computeSignature
2349
- if ! mySignature.isUnderDefined && ! isProvisional then mySignatureRunId = ctx.runId
2433
+ mySignatureProvState = currentProvisionalState
2434
+ if ! mySignature.isUnderDefined then mySignatureRunId = ctx.runId
2350
2435
mySignature
2351
2436
end signature
2352
2437
@@ -2357,7 +2442,9 @@ object Types extends TypeUtils {
2357
2442
* some symbols change their signature at erasure.
2358
2443
*/
2359
2444
private def currentSignature (using Context ): Signature =
2360
- if ctx.runId == mySignatureRunId then mySignature
2445
+ if ctx.runId == mySignatureRunId
2446
+ && isCacheUpToDate(currentProvisionalState, mySignatureProvState)
2447
+ then mySignature
2361
2448
else
2362
2449
val lastd = lastDenotation
2363
2450
if lastd != null then sigFromDenot(lastd)
@@ -2435,7 +2522,10 @@ object Types extends TypeUtils {
2435
2522
val lastd = lastDenotation.asInstanceOf [Denotation ]
2436
2523
// Even if checkedPeriod == now we still need to recheck lastDenotation.validFor
2437
2524
// as it may have been mutated by SymDenotation#installAfter
2438
- if checkedPeriod.code != NowhereCode && lastd.validFor.contains(ctx.period) then lastd
2525
+ if checkedPeriod.code != NowhereCode
2526
+ && isCacheUpToDate(prefix.currentProvisionalState, lastDenotationProvState)
2527
+ && lastd.validFor.contains(ctx.period)
2528
+ then lastd
2439
2529
else computeDenot
2440
2530
2441
2531
private def computeDenot (using Context ): Denotation = {
@@ -2469,14 +2559,18 @@ object Types extends TypeUtils {
2469
2559
finish(symd.current)
2470
2560
}
2471
2561
2562
+ def isLastDenotValid =
2563
+ checkedPeriod.code != NowhereCode
2564
+ && isCacheUpToDate(prefix.currentProvisionalState, lastDenotationProvState)
2565
+
2472
2566
lastDenotation match {
2473
2567
case lastd0 : SingleDenotation =>
2474
2568
val lastd = lastd0.skipRemoved
2475
- if lastd.validFor.runId == ctx.runId && checkedPeriod.code != NowhereCode then
2569
+ if lastd.validFor.runId == ctx.runId && isLastDenotValid then
2476
2570
finish(lastd.current)
2477
2571
else lastd match {
2478
2572
case lastd : SymDenotation =>
2479
- if stillValid(lastd) && checkedPeriod.code != NowhereCode then finish(lastd.current)
2573
+ if stillValid(lastd) && isLastDenotValid then finish(lastd.current)
2480
2574
else finish(memberDenot(lastd.initial.name, allowPrivate = false ))
2481
2575
case _ =>
2482
2576
fromDesignator
@@ -2567,7 +2661,8 @@ object Types extends TypeUtils {
2567
2661
2568
2662
lastDenotation = denot
2569
2663
lastSymbol = denot.symbol
2570
- checkedPeriod = if (prefix.isProvisional) Nowhere else ctx.period
2664
+ lastDenotationProvState = prefix.currentProvisionalState
2665
+ checkedPeriod = ctx.period
2571
2666
designator match {
2572
2667
case sym : Symbol if designator ne lastSymbol.nn =>
2573
2668
designator = lastSymbol.asInstanceOf [Designator { type ThisName = self.ThisName }]
@@ -3850,14 +3945,18 @@ object Types extends TypeUtils {
3850
3945
sealed abstract class MethodOrPoly extends UncachedGroundType with LambdaType with MethodicType {
3851
3946
3852
3947
// Invariants:
3853
- // (1) mySignatureRunId != NoRunId => mySignature != null
3854
- // (2) myJavaSignatureRunId != NoRunId => myJavaSignature != null
3948
+ // (1) mySignatureRunId != NoRunId => mySignature != null
3949
+ // (2) myJavaSignatureRunId != NoRunId => myJavaSignature != null
3950
+ // (3) myScala2SignatureRunId != NoRunId => myScala2Signature != null
3855
3951
3856
3952
private var mySignature : Signature = uninitialized
3953
+ private var mySignatureProvState : ProvisionalState | Null = null
3857
3954
private var mySignatureRunId : Int = NoRunId
3858
3955
private var myJavaSignature : Signature = uninitialized
3956
+ private var myJavaSignatureProvState : ProvisionalState | Null = null
3859
3957
private var myJavaSignatureRunId : Int = NoRunId
3860
3958
private var myScala2Signature : Signature = uninitialized
3959
+ private var myScala2SignatureProvState : ProvisionalState | Null = null
3861
3960
private var myScala2SignatureRunId : Int = NoRunId
3862
3961
3863
3962
/** If `isJava` is false, the Scala signature of this method. Otherwise, its Java signature.
@@ -3895,19 +3994,25 @@ object Types extends TypeUtils {
3895
3994
3896
3995
sourceLanguage match
3897
3996
case SourceLanguage .Java =>
3898
- if ctx.runId != myJavaSignatureRunId then
3997
+ if ctx.runId != myJavaSignatureRunId
3998
+ || ! isCacheUpToDate(currentProvisionalState, myJavaSignatureProvState) then
3899
3999
myJavaSignature = computeSignature
3900
- if ! myJavaSignature.isUnderDefined && ! isProvisional then myJavaSignatureRunId = ctx.runId
4000
+ myJavaSignatureProvState = currentProvisionalState
4001
+ if ! myJavaSignature.isUnderDefined then myJavaSignatureRunId = ctx.runId
3901
4002
myJavaSignature
3902
4003
case SourceLanguage .Scala2 =>
3903
- if ctx.runId != myScala2SignatureRunId then
4004
+ if ctx.runId != myScala2SignatureRunId
4005
+ || ! isCacheUpToDate(currentProvisionalState, myScala2SignatureProvState) then
3904
4006
myScala2Signature = computeSignature
3905
- if ! myScala2Signature.isUnderDefined && ! isProvisional then myScala2SignatureRunId = ctx.runId
4007
+ myScala2SignatureProvState = currentProvisionalState
4008
+ if ! myScala2Signature.isUnderDefined then myScala2SignatureRunId = ctx.runId
3906
4009
myScala2Signature
3907
4010
case SourceLanguage .Scala3 =>
3908
- if ctx.runId != mySignatureRunId then
4011
+ if ctx.runId != mySignatureRunId
4012
+ || ! isCacheUpToDate(currentProvisionalState, mySignatureProvState) then
3909
4013
mySignature = computeSignature
3910
- if ! mySignature.isUnderDefined && ! isProvisional then mySignatureRunId = ctx.runId
4014
+ mySignatureProvState = currentProvisionalState
4015
+ if ! mySignature.isUnderDefined then mySignatureRunId = ctx.runId
3911
4016
mySignature
3912
4017
end signature
3913
4018
0 commit comments