@@ -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 )
@@ -1309,7 +1396,10 @@ object Types extends TypeUtils {
1309
1396
final def widen (using Context ): Type = this match
1310
1397
case _ : TypeRef | _ : MethodOrPoly => this // fast path for most frequent cases
1311
1398
case tp : TermRef => // fast path for next most frequent case
1312
- if tp.isOverloaded then tp else tp.underlying.widen
1399
+ // Don't call `isOverloaded` and `underlying` on `tp` directly,
1400
+ // to avoid computing provisional state twice.
1401
+ val denot = tp.denot
1402
+ if denot.isOverloaded then tp else denot.info.widen
1313
1403
case tp : SingletonType => tp.underlying.widen
1314
1404
case tp : ExprType => tp.resultType.widen
1315
1405
case tp =>
@@ -2303,10 +2393,12 @@ object Types extends TypeUtils {
2303
2393
2304
2394
private var myName : Name | Null = null
2305
2395
private var lastDenotation : Denotation | Null = null
2396
+ private var lastDenotationProvState : ProvisionalState | Null = null
2306
2397
private var lastSymbol : Symbol | Null = null
2307
2398
private var checkedPeriod : Period = Nowhere
2308
2399
private var myStableHash : Byte = 0
2309
2400
private var mySignature : Signature = uninitialized
2401
+ private var mySignatureProvState : ProvisionalState | Null = null
2310
2402
private var mySignatureRunId : Int = NoRunId
2311
2403
2312
2404
// Invariants:
@@ -2341,9 +2433,12 @@ object Types extends TypeUtils {
2341
2433
else if ctx.erasedTypes then atPhase(erasurePhase)(computeSignature)
2342
2434
else symbol.asSeenFrom(prefix).signature
2343
2435
2344
- if ctx.runId != mySignatureRunId then
2436
+ val currentState = currentProvisionalState
2437
+ if ctx.runId != mySignatureRunId
2438
+ || ! isStateUpToDate(currentState, mySignatureProvState) then
2345
2439
mySignature = computeSignature
2346
- if ! mySignature.isUnderDefined && ! isProvisional then mySignatureRunId = ctx.runId
2440
+ mySignatureProvState = currentState
2441
+ if ! mySignature.isUnderDefined then mySignatureRunId = ctx.runId
2347
2442
mySignature
2348
2443
end signature
2349
2444
@@ -2354,7 +2449,9 @@ object Types extends TypeUtils {
2354
2449
* some symbols change their signature at erasure.
2355
2450
*/
2356
2451
private def currentSignature (using Context ): Signature =
2357
- if ctx.runId == mySignatureRunId then mySignature
2452
+ if ctx.runId == mySignatureRunId
2453
+ && isStateUpToDate(currentProvisionalState, mySignatureProvState)
2454
+ then mySignature
2358
2455
else
2359
2456
val lastd = lastDenotation
2360
2457
if lastd != null then sigFromDenot(lastd)
@@ -2432,7 +2529,10 @@ object Types extends TypeUtils {
2432
2529
val lastd = lastDenotation.asInstanceOf [Denotation ]
2433
2530
// Even if checkedPeriod == now we still need to recheck lastDenotation.validFor
2434
2531
// as it may have been mutated by SymDenotation#installAfter
2435
- if checkedPeriod.code != NowhereCode && lastd.validFor.contains(ctx.period) then lastd
2532
+ if checkedPeriod.code != NowhereCode
2533
+ && isStateUpToDate(prefix.currentProvisionalState, lastDenotationProvState)
2534
+ && lastd.validFor.contains(ctx.period)
2535
+ then lastd
2436
2536
else computeDenot
2437
2537
2438
2538
private def computeDenot (using Context ): Denotation = {
@@ -2466,14 +2566,18 @@ object Types extends TypeUtils {
2466
2566
finish(symd.current)
2467
2567
}
2468
2568
2569
+ def isLastDenotValid =
2570
+ checkedPeriod.code != NowhereCode
2571
+ && isStateUpToDate(prefix.currentProvisionalState, lastDenotationProvState)
2572
+
2469
2573
lastDenotation match {
2470
2574
case lastd0 : SingleDenotation =>
2471
2575
val lastd = lastd0.skipRemoved
2472
- if lastd.validFor.runId == ctx.runId && checkedPeriod.code != NowhereCode then
2576
+ if lastd.validFor.runId == ctx.runId && isLastDenotValid then
2473
2577
finish(lastd.current)
2474
2578
else lastd match {
2475
2579
case lastd : SymDenotation =>
2476
- if stillValid(lastd) && checkedPeriod.code != NowhereCode then finish(lastd.current)
2580
+ if stillValid(lastd) && isLastDenotValid then finish(lastd.current)
2477
2581
else finish(memberDenot(lastd.initial.name, allowPrivate = false ))
2478
2582
case _ =>
2479
2583
fromDesignator
@@ -2564,7 +2668,8 @@ object Types extends TypeUtils {
2564
2668
2565
2669
lastDenotation = denot
2566
2670
lastSymbol = denot.symbol
2567
- checkedPeriod = if (prefix.isProvisional) Nowhere else ctx.period
2671
+ lastDenotationProvState = prefix.currentProvisionalState
2672
+ checkedPeriod = ctx.period
2568
2673
designator match {
2569
2674
case sym : Symbol if designator ne lastSymbol.nn =>
2570
2675
designator = lastSymbol.asInstanceOf [Designator { type ThisName = self.ThisName }]
@@ -3847,14 +3952,18 @@ object Types extends TypeUtils {
3847
3952
sealed abstract class MethodOrPoly extends UncachedGroundType with LambdaType with MethodicType {
3848
3953
3849
3954
// Invariants:
3850
- // (1) mySignatureRunId != NoRunId => mySignature != null
3851
- // (2) myJavaSignatureRunId != NoRunId => myJavaSignature != null
3955
+ // (1) mySignatureRunId != NoRunId => mySignature != null
3956
+ // (2) myJavaSignatureRunId != NoRunId => myJavaSignature != null
3957
+ // (3) myScala2SignatureRunId != NoRunId => myScala2Signature != null
3852
3958
3853
3959
private var mySignature : Signature = uninitialized
3960
+ private var mySignatureProvState : ProvisionalState | Null = null
3854
3961
private var mySignatureRunId : Int = NoRunId
3855
3962
private var myJavaSignature : Signature = uninitialized
3963
+ private var myJavaSignatureProvState : ProvisionalState | Null = null
3856
3964
private var myJavaSignatureRunId : Int = NoRunId
3857
3965
private var myScala2Signature : Signature = uninitialized
3966
+ private var myScala2SignatureProvState : ProvisionalState | Null = null
3858
3967
private var myScala2SignatureRunId : Int = NoRunId
3859
3968
3860
3969
/** If `isJava` is false, the Scala signature of this method. Otherwise, its Java signature.
@@ -3890,21 +3999,28 @@ object Types extends TypeUtils {
3890
3999
case tp : PolyType =>
3891
4000
resultSignature.prependTypeParams(tp.paramNames.length)
3892
4001
4002
+ val currentState = currentProvisionalState
3893
4003
sourceLanguage match
3894
4004
case SourceLanguage .Java =>
3895
- if ctx.runId != myJavaSignatureRunId then
4005
+ if ctx.runId != myJavaSignatureRunId
4006
+ || ! isStateUpToDate(currentState, myJavaSignatureProvState) then
3896
4007
myJavaSignature = computeSignature
3897
- if ! myJavaSignature.isUnderDefined && ! isProvisional then myJavaSignatureRunId = ctx.runId
4008
+ myJavaSignatureProvState = currentState
4009
+ if ! myJavaSignature.isUnderDefined then myJavaSignatureRunId = ctx.runId
3898
4010
myJavaSignature
3899
4011
case SourceLanguage .Scala2 =>
3900
- if ctx.runId != myScala2SignatureRunId then
4012
+ if ctx.runId != myScala2SignatureRunId
4013
+ || ! isStateUpToDate(currentState, myScala2SignatureProvState) then
3901
4014
myScala2Signature = computeSignature
3902
- if ! myScala2Signature.isUnderDefined && ! isProvisional then myScala2SignatureRunId = ctx.runId
4015
+ myScala2SignatureProvState = currentState
4016
+ if ! myScala2Signature.isUnderDefined then myScala2SignatureRunId = ctx.runId
3903
4017
myScala2Signature
3904
4018
case SourceLanguage .Scala3 =>
3905
- if ctx.runId != mySignatureRunId then
4019
+ if ctx.runId != mySignatureRunId
4020
+ || ! isStateUpToDate(currentState, mySignatureProvState) then
3906
4021
mySignature = computeSignature
3907
- if ! mySignature.isUnderDefined && ! isProvisional then mySignatureRunId = ctx.runId
4022
+ mySignatureProvState = currentState
4023
+ if ! mySignature.isUnderDefined then mySignatureRunId = ctx.runId
3908
4024
mySignature
3909
4025
end signature
3910
4026
0 commit comments