Skip to content

Commit 61857a7

Browse files
committed
Refactor TermRef widening logic; cache widening results during overloading resolving.
1 parent bad02ab commit 61857a7

File tree

2 files changed

+32
-21
lines changed

2 files changed

+32
-21
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1309,7 +1309,10 @@ object Types extends TypeUtils {
13091309
final def widen(using Context): Type = this match
13101310
case _: TypeRef | _: MethodOrPoly => this // fast path for most frequent cases
13111311
case tp: TermRef => // fast path for next most frequent case
1312-
if tp.isOverloaded then tp else tp.underlying.widen
1312+
// Don't call `isOverloaded` and `underlying` on `tp` directly,
1313+
// because `denot` will be computed twice if the type is provisional.
1314+
val denot = tp.denot
1315+
if denot.isOverloaded then tp else denot.info.widen
13131316
case tp: SingletonType => tp.underlying.widen
13141317
case tp: ExprType => tp.resultType.widen
13151318
case tp =>

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2048,6 +2048,13 @@ trait Applications extends Compatibility {
20482048
def resolveOverloaded(alts: List[TermRef], pt: Type)(using Context): List[TermRef] =
20492049
record("resolveOverloaded")
20502050

2051+
// A local cache for widenings of alternatives.
2052+
// If a type is provisonal, its denotation will not cached.
2053+
// Hence, using `widen` on provisional types everywhere will recompute denotations repeatedly.
2054+
// Given the denotation will not change in this part, we can safely cache the result.
2055+
val altsWidenMap = mutable.HashMap.empty[TermRef, Type]
2056+
def widen(ref: TermRef): Type = altsWidenMap.getOrElseUpdate(ref, ref.widen)
2057+
20512058
/** Is `alt` a method or polytype whose result type after the first value parameter
20522059
* section conforms to the expected type `resultType`? If `resultType`
20532060
* is a `IgnoredProto`, pick the underlying type instead.
@@ -2095,15 +2102,15 @@ trait Applications extends Compatibility {
20952102
pt match
20962103
case pt: FunProto =>
20972104
if pt.applyKind == ApplyKind.Using then
2098-
val alts0 = alts.filterConserve(_.widen.stripPoly.isImplicitMethod)
2105+
val alts0 = alts.filterConserve(alt => widen(alt).stripPoly.isImplicitMethod)
20992106
if alts0 ne alts then return resolve(alts0)
2100-
else if alts.exists(_.widen.stripPoly.isContextualMethod) then
2101-
return resolveMapped(alts, alt => stripImplicit(alt.widen), pt)
2107+
else if alts.exists(alt => widen(alt).stripPoly.isContextualMethod) then
2108+
return resolveMapped(alts, alt => stripImplicit(widen(alt)), pt)
21022109
case _ =>
21032110

2104-
var found = withoutMode(Mode.ImplicitsEnabled)(resolveOverloaded1(alts, pt))
2111+
var found = withoutMode(Mode.ImplicitsEnabled)(resolveOverloaded1(alts, pt, altsWidenMap))
21052112
if found.isEmpty && ctx.mode.is(Mode.ImplicitsEnabled) then
2106-
found = resolveOverloaded1(alts, pt)
2113+
found = resolveOverloaded1(alts, pt, altsWidenMap)
21072114
found match
21082115
case alt :: Nil => adaptByResult(alt, alts) :: Nil
21092116
case _ => found
@@ -2113,16 +2120,15 @@ trait Applications extends Compatibility {
21132120
* - the result is applied to value arguments and alternative is not a method, or
21142121
* - the result is applied to type arguments and alternative is not polymorphic
21152122
*/
2116-
val tryApply: Type => Boolean = alt => pt match {
2117-
case pt: FunProto => !alt.widen.stripPoly.isInstanceOf[MethodType]
2118-
case pt: PolyProto => !alt.widen.isInstanceOf[PolyType]
2123+
def tryApply(alt: TermRef): Boolean = pt match
2124+
case pt: FunProto => !widen(alt).stripPoly.isInstanceOf[MethodType]
2125+
case pt: PolyProto => !widen(alt).isInstanceOf[PolyType]
21192126
case _ => false
2120-
}
21212127

21222128
/** Replace each alternative by its apply members where necessary */
21232129
def applyMembers(alt: TermRef): List[TermRef] =
21242130
if (tryApply(alt)) {
2125-
val qual = alt.widen match {
2131+
val qual = widen(alt) match {
21262132
case pt: PolyType =>
21272133
wildApprox(pt.resultType)
21282134
case _ =>
@@ -2150,10 +2156,12 @@ trait Applications extends Compatibility {
21502156
* It might be called twice from the public `resolveOverloaded` method, once with
21512157
* implicits and SAM conversions enabled, and once without.
21522158
*/
2153-
private def resolveOverloaded1(alts: List[TermRef], pt: Type)(using Context): List[TermRef] =
2159+
private def resolveOverloaded1(alts: List[TermRef], pt: Type, altsWidenMap: mutable.HashMap[TermRef, Type])(using Context): List[TermRef] =
21542160
trace(i"resolve over $alts%, %, pt = $pt", typr, show = true) {
21552161
record(s"resolveOverloaded1", alts.length)
21562162

2163+
def widen(ref: TermRef): Type = altsWidenMap.getOrElseUpdate(ref, ref.widen)
2164+
21572165
def isDetermined(alts: List[TermRef]) = alts.isEmpty || alts.tail.isEmpty
21582166

21592167
/** The shape of given tree as a type; cannot handle named arguments. */
@@ -2197,7 +2205,7 @@ trait Applications extends Compatibility {
21972205
// If ref refers to a method whose parameter at index `idx` is a function type,
21982206
// the arity of that function, otherise -1.
21992207
def paramCount(ref: TermRef) =
2200-
val formals = ref.widen.firstParamTypes
2208+
val formals = widen(ref).firstParamTypes
22012209
if formals.length > idx then
22022210
formals(idx).dealias match
22032211
case defn.FunctionNOf(args, _, _) => args.length
@@ -2222,7 +2230,7 @@ trait Applications extends Compatibility {
22222230
val candidates = pt match {
22232231
case pt @ FunProto(args, resultType) =>
22242232
val numArgs = args.length
2225-
def sizeFits(alt: TermRef): Boolean = alt.widen.stripPoly match {
2233+
def sizeFits(alt: TermRef): Boolean = widen(alt).stripPoly match {
22262234
case tp: MethodType =>
22272235
val ptypes = tp.paramInfos
22282236
val numParams = ptypes.length
@@ -2277,12 +2285,12 @@ trait Applications extends Compatibility {
22772285
val alts1 = alts.filterConserve(pt.canInstantiate)
22782286
if isDetermined(alts1) then alts1
22792287
else
2280-
def withinBounds(alt: TermRef) = alt.widen match
2288+
def withinBounds(alt: TermRef) = widen(alt) match
22812289
case tp: PolyType =>
22822290
TypeOps.boundsViolations(targs1, tp.paramInfos, _.substParams(tp, _), NoType).isEmpty
22832291
val alts2 = alts1.filter(withinBounds)
22842292
if isDetermined(alts2) then alts2
2285-
else resolveMapped(alts1, _.widen.appliedTo(targs1.tpes), pt1)
2293+
else resolveMapped(alts1, alt => widen(alt).appliedTo(targs1.tpes), pt1)
22862294

22872295
case pt =>
22882296
val compat = alts.filterConserve(normalizedCompatible(_, pt, keepConstraint = false))
@@ -2327,9 +2335,9 @@ trait Applications extends Compatibility {
23272335
case _ =>
23282336
NoType
23292337
}
2330-
skip(alt.widen)
2338+
skip(widen(alt))
23312339

2332-
def resultIsMethod(tp: Type): Boolean = tp.widen.stripPoly match
2340+
def resultIsMethod(tp: TermRef): Boolean = widen(tp).stripPoly match
23332341
case tp: MethodType => stripInferrable(tp.resultType).isInstanceOf[MethodType]
23342342
case _ => false
23352343

@@ -2359,18 +2367,18 @@ trait Applications extends Compatibility {
23592367
if noCurriedCount == 1 then
23602368
noCurried
23612369
else if noCurriedCount > 1 && noCurriedCount < alts.length then
2362-
resolveOverloaded1(noCurried, pt)
2370+
resolveOverloaded1(noCurried, pt, altsWidenMap)
23632371
else
23642372
// prefer alternatves that match without default parameters
23652373
val noDefaults = alts.filterConserve(!_.symbol.hasDefaultParams)
23662374
val noDefaultsCount = noDefaults.length
23672375
if noDefaultsCount == 1 then
23682376
noDefaults
23692377
else if noDefaultsCount > 1 && noDefaultsCount < alts.length then
2370-
resolveOverloaded1(noDefaults, pt)
2378+
resolveOverloaded1(noDefaults, pt, altsWidenMap)
23712379
else if deepPt ne pt then
23722380
// try again with a deeper known expected type
2373-
resolveOverloaded1(alts, deepPt)
2381+
resolveOverloaded1(alts, deepPt, altsWidenMap)
23742382
else
23752383
candidates
23762384
}

0 commit comments

Comments
 (0)