@@ -118,39 +118,126 @@ object Types extends TypeUtils {
118
118
* a call to `resetInst`. This means all caches that rely on `isProvisional`
119
119
* can likely end up returning stale results.
120
120
*/
121
- def isProvisional (using Context ): Boolean = mightBeProvisional && testProvisional
121
+ // def isProvisional(using Context): Boolean = mightBeProvisional && testProvisional
122
+ def isProvisional (using Context ): Boolean = mightBeProvisional && ! currentProvisionalState.isEmpty
122
123
123
- private def testProvisional (using Context ): Boolean =
124
+ type ProvisionalState = util.HashMap [Type , Type ]
125
+
126
+ def currentProvisionalState (using Context ): ProvisionalState =
127
+ val state : ProvisionalState = util.HashMap ()
128
+ // Compared to `testProvisional`, we don't use short-circuiting or,
129
+ // because we want to collect all provisional types.
124
130
class ProAcc extends TypeAccumulator [Boolean ]:
125
- override def apply (x : Boolean , t : Type ) = x || test(t, this )
131
+ override def apply (x : Boolean , t : Type ) = x | test(t, this )
126
132
def test (t : Type , theAcc : TypeAccumulator [Boolean ] | Null ): Boolean =
127
133
if t.mightBeProvisional then
128
134
t.mightBeProvisional = t match
129
135
case t : TypeRef =>
130
- t.currentSymbol.isProvisional || ! t.currentSymbol.isStatic && {
136
+ if t.currentSymbol.isProvisional then
137
+ // When t is a TypeRef and its symbol is provisional,
138
+ // t will be considered provisional and its state is always updating.
139
+ // We store itself as info.
140
+ state(t) = t
141
+ true
142
+ else if ! t.currentSymbol.isStatic then
131
143
(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
- }
144
+ if test(t.prefix, theAcc) then
145
+ // If the prefix is provisional, some provisional type from it
146
+ // must have been added to state, so we don't need to add t.
147
+ true
148
+ else t.denot.infoOrCompleter.match
149
+ case info : LazyType =>
150
+ state(t) = info
151
+ true
152
+ case info : AliasingBounds =>
153
+ test(info.alias, theAcc)
154
+ case TypeBounds (lo, hi) =>
155
+ test(lo, theAcc) | test(hi, theAcc)
156
+ case _ =>
157
+ // If a TypeRef has been fully completed, it is no longer provisional,
158
+ // so we don't need to traverse its info.
159
+ false
160
+ else false
139
161
case t : TermRef =>
140
162
! t.currentSymbol.isStatic && test(t.prefix, theAcc)
141
163
case t : AppliedType =>
142
- t.fold(false , (x, tp) => x || test(tp, theAcc))
164
+ t.fold(false , (x, tp) => x | test(tp, theAcc))
143
165
case t : TypeVar =>
144
- ! t.isPermanentlyInstantiated || test(t.permanentInst, theAcc)
166
+ if t.isPermanentlyInstantiated then
167
+ test(t.permanentInst, theAcc)
168
+ else
169
+ val inst = t.instanceOpt
170
+ if inst.exists then
171
+ // We want to store the temporary instance to the state
172
+ // in order to reuse the cache when possible.
173
+ state(t) = inst
174
+ test(inst, theAcc)
175
+ else
176
+ // When t is a TypeVar and does not have an instancication,
177
+ // we store itself as info.
178
+ state(t) = t
179
+ true
145
180
case t : LazyRef =>
146
- ! t.completed || test(t.ref, theAcc)
181
+ if ! t.completed then
182
+ // When t is a LazyRef and is not completed,
183
+ // we store itself as info.
184
+ state(t) = t
185
+ true
186
+ else
187
+ test(t.ref, theAcc)
147
188
case _ =>
148
189
(if theAcc != null then theAcc else ProAcc ()).foldOver(false , t)
149
190
end if
150
191
t.mightBeProvisional
151
192
end test
152
193
test(this , null )
153
- end testProvisional
194
+ state
195
+ end currentProvisionalState
196
+
197
+ def isStateUpToDate (
198
+ currentState : ProvisionalState ,
199
+ lastState : ProvisionalState | Null )
200
+ (using Context ): Boolean =
201
+ lastState != null
202
+ && currentState.size == lastState.size
203
+ && currentState.iterator.forall: (tp, info) =>
204
+ lastState.contains(tp) && {
205
+ tp match
206
+ case tp : TypeRef => (info ne tp) && (info eq lastState(tp))
207
+ case _=> info eq lastState(tp)
208
+ }
209
+
210
+ // private def testProvisional(using Context): Boolean =
211
+ // class ProAcc extends TypeAccumulator[Boolean]:
212
+ // override def apply(x: Boolean, t: Type) = x || test(t, this)
213
+ // def test(t: Type, theAcc: TypeAccumulator[Boolean] | Null): Boolean =
214
+ // if t.mightBeProvisional then
215
+ // t.mightBeProvisional = t match
216
+ // case t: TypeRef =>
217
+ // t.currentSymbol.isProvisional || !t.currentSymbol.isStatic && {
218
+ // (t: Type).mightBeProvisional = false // break cycles
219
+ // test(t.prefix, theAcc)
220
+ // || t.denot.infoOrCompleter.match
221
+ // case info: LazyType => true
222
+ // case info: AliasingBounds => test(info.alias, theAcc)
223
+ // case TypeBounds(lo, hi) => test(lo, theAcc) || test(hi, theAcc)
224
+ // case _ => false
225
+ // }
226
+ // case t: TermRef =>
227
+ // !t.currentSymbol.isStatic && test(t.prefix, theAcc)
228
+ // case t: AppliedType =>
229
+ // t.fold(false, (x, tp) => x || test(tp, theAcc))
230
+ // case t: TypeVar =>
231
+ // !t.isPermanentlyInstantiated || test(t.permanentInst, theAcc)
232
+ // case t: LazyRef =>
233
+ // !t.completed || test(t.ref, theAcc)
234
+ // case _ =>
235
+ // (if theAcc != null then theAcc else ProAcc()).foldOver(false, t)
236
+ // end if
237
+ // t.mightBeProvisional
238
+ // end test
239
+ // test(this, null)
240
+ // end testProvisional
154
241
155
242
/** Is this type different from NoType? */
156
243
final def exists : Boolean = this .ne(NoType )
@@ -1311,7 +1398,10 @@ object Types extends TypeUtils {
1311
1398
final def widen (using Context ): Type = this match
1312
1399
case _ : TypeRef | _ : MethodOrPoly => this // fast path for most frequent cases
1313
1400
case tp : TermRef => // fast path for next most frequent case
1314
- if tp.isOverloaded then tp else tp.underlying.widen
1401
+ // Don't call `isOverloaded` and `underlying` on `tp` directly,
1402
+ // to avoid computing provisional state twice.
1403
+ val denot = tp.denot
1404
+ if denot.isOverloaded then tp else denot.info.widen
1315
1405
case tp : SingletonType => tp.underlying.widen
1316
1406
case tp : ExprType => tp.resultType.widen
1317
1407
case tp =>
@@ -2305,10 +2395,12 @@ object Types extends TypeUtils {
2305
2395
2306
2396
private var myName : Name | Null = null
2307
2397
private var lastDenotation : Denotation | Null = null
2398
+ private var lastDenotationProvState : ProvisionalState | Null = null
2308
2399
private var lastSymbol : Symbol | Null = null
2309
2400
private var checkedPeriod : Period = Nowhere
2310
2401
private var myStableHash : Byte = 0
2311
2402
private var mySignature : Signature = uninitialized
2403
+ private var mySignatureProvState : ProvisionalState | Null = null
2312
2404
private var mySignatureRunId : Int = NoRunId
2313
2405
2314
2406
// Invariants:
@@ -2343,9 +2435,12 @@ object Types extends TypeUtils {
2343
2435
else if ctx.erasedTypes then atPhase(erasurePhase)(computeSignature)
2344
2436
else symbol.asSeenFrom(prefix).signature
2345
2437
2346
- if ctx.runId != mySignatureRunId then
2438
+ val currentState = currentProvisionalState
2439
+ if ctx.runId != mySignatureRunId
2440
+ || ! isStateUpToDate(currentState, mySignatureProvState) then
2347
2441
mySignature = computeSignature
2348
- if ! mySignature.isUnderDefined && ! isProvisional then mySignatureRunId = ctx.runId
2442
+ mySignatureProvState = currentState
2443
+ if ! mySignature.isUnderDefined then mySignatureRunId = ctx.runId
2349
2444
mySignature
2350
2445
end signature
2351
2446
@@ -2356,7 +2451,9 @@ object Types extends TypeUtils {
2356
2451
* some symbols change their signature at erasure.
2357
2452
*/
2358
2453
private def currentSignature (using Context ): Signature =
2359
- if ctx.runId == mySignatureRunId then mySignature
2454
+ if ctx.runId == mySignatureRunId
2455
+ && isStateUpToDate(currentProvisionalState, mySignatureProvState)
2456
+ then mySignature
2360
2457
else
2361
2458
val lastd = lastDenotation
2362
2459
if lastd != null then sigFromDenot(lastd)
@@ -2434,7 +2531,10 @@ object Types extends TypeUtils {
2434
2531
val lastd = lastDenotation.asInstanceOf [Denotation ]
2435
2532
// Even if checkedPeriod == now we still need to recheck lastDenotation.validFor
2436
2533
// as it may have been mutated by SymDenotation#installAfter
2437
- if checkedPeriod.code != NowhereCode && lastd.validFor.contains(ctx.period) then lastd
2534
+ if checkedPeriod.code != NowhereCode
2535
+ && isStateUpToDate(prefix.currentProvisionalState, lastDenotationProvState)
2536
+ && lastd.validFor.contains(ctx.period)
2537
+ then lastd
2438
2538
else computeDenot
2439
2539
2440
2540
private def computeDenot (using Context ): Denotation = {
@@ -2468,14 +2568,18 @@ object Types extends TypeUtils {
2468
2568
finish(symd.current)
2469
2569
}
2470
2570
2571
+ def isLastDenotValid =
2572
+ checkedPeriod.code != NowhereCode
2573
+ && isStateUpToDate(prefix.currentProvisionalState, lastDenotationProvState)
2574
+
2471
2575
lastDenotation match {
2472
2576
case lastd0 : SingleDenotation =>
2473
2577
val lastd = lastd0.skipRemoved
2474
- if lastd.validFor.runId == ctx.runId && checkedPeriod.code != NowhereCode then
2578
+ if lastd.validFor.runId == ctx.runId && isLastDenotValid then
2475
2579
finish(lastd.current)
2476
2580
else lastd match {
2477
2581
case lastd : SymDenotation =>
2478
- if stillValid(lastd) && checkedPeriod.code != NowhereCode then finish(lastd.current)
2582
+ if stillValid(lastd) && isLastDenotValid then finish(lastd.current)
2479
2583
else finish(memberDenot(lastd.initial.name, allowPrivate = false ))
2480
2584
case _ =>
2481
2585
fromDesignator
@@ -2566,7 +2670,8 @@ object Types extends TypeUtils {
2566
2670
2567
2671
lastDenotation = denot
2568
2672
lastSymbol = denot.symbol
2569
- checkedPeriod = if (prefix.isProvisional) Nowhere else ctx.period
2673
+ lastDenotationProvState = prefix.currentProvisionalState
2674
+ checkedPeriod = ctx.period
2570
2675
designator match {
2571
2676
case sym : Symbol if designator ne lastSymbol.nn =>
2572
2677
designator = lastSymbol.asInstanceOf [Designator { type ThisName = self.ThisName }]
@@ -3849,14 +3954,18 @@ object Types extends TypeUtils {
3849
3954
sealed abstract class MethodOrPoly extends UncachedGroundType with LambdaType with MethodicType {
3850
3955
3851
3956
// Invariants:
3852
- // (1) mySignatureRunId != NoRunId => mySignature != null
3853
- // (2) myJavaSignatureRunId != NoRunId => myJavaSignature != null
3957
+ // (1) mySignatureRunId != NoRunId => mySignature != null
3958
+ // (2) myJavaSignatureRunId != NoRunId => myJavaSignature != null
3959
+ // (3) myScala2SignatureRunId != NoRunId => myScala2Signature != null
3854
3960
3855
3961
private var mySignature : Signature = uninitialized
3962
+ private var mySignatureProvState : ProvisionalState | Null = null
3856
3963
private var mySignatureRunId : Int = NoRunId
3857
3964
private var myJavaSignature : Signature = uninitialized
3965
+ private var myJavaSignatureProvState : ProvisionalState | Null = null
3858
3966
private var myJavaSignatureRunId : Int = NoRunId
3859
3967
private var myScala2Signature : Signature = uninitialized
3968
+ private var myScala2SignatureProvState : ProvisionalState | Null = null
3860
3969
private var myScala2SignatureRunId : Int = NoRunId
3861
3970
3862
3971
/** If `isJava` is false, the Scala signature of this method. Otherwise, its Java signature.
@@ -3892,21 +4001,28 @@ object Types extends TypeUtils {
3892
4001
case tp : PolyType =>
3893
4002
resultSignature.prependTypeParams(tp.paramNames.length)
3894
4003
4004
+ val currentState = currentProvisionalState
3895
4005
sourceLanguage match
3896
4006
case SourceLanguage .Java =>
3897
- if ctx.runId != myJavaSignatureRunId then
4007
+ if ctx.runId != myJavaSignatureRunId
4008
+ || ! isStateUpToDate(currentState, myJavaSignatureProvState) then
3898
4009
myJavaSignature = computeSignature
3899
- if ! myJavaSignature.isUnderDefined && ! isProvisional then myJavaSignatureRunId = ctx.runId
4010
+ myJavaSignatureProvState = currentState
4011
+ if ! myJavaSignature.isUnderDefined then myJavaSignatureRunId = ctx.runId
3900
4012
myJavaSignature
3901
4013
case SourceLanguage .Scala2 =>
3902
- if ctx.runId != myScala2SignatureRunId then
4014
+ if ctx.runId != myScala2SignatureRunId
4015
+ || ! isStateUpToDate(currentState, myScala2SignatureProvState) then
3903
4016
myScala2Signature = computeSignature
3904
- if ! myScala2Signature.isUnderDefined && ! isProvisional then myScala2SignatureRunId = ctx.runId
4017
+ myScala2SignatureProvState = currentState
4018
+ if ! myScala2Signature.isUnderDefined then myScala2SignatureRunId = ctx.runId
3905
4019
myScala2Signature
3906
4020
case SourceLanguage .Scala3 =>
3907
- if ctx.runId != mySignatureRunId then
4021
+ if ctx.runId != mySignatureRunId
4022
+ || ! isStateUpToDate(currentState, mySignatureProvState) then
3908
4023
mySignature = computeSignature
3909
- if ! mySignature.isUnderDefined && ! isProvisional then mySignatureRunId = ctx.runId
4024
+ mySignatureProvState = currentState
4025
+ if ! mySignature.isUnderDefined then mySignatureRunId = ctx.runId
3910
4026
mySignature
3911
4027
end signature
3912
4028
0 commit comments