Skip to content

Commit d06d5ae

Browse files
committed
Replace isAsGood with ternary operator
The aim is to have ultimately fewer comparisons. The downside is that some of the comparisons are now more expensive, because types have to be compared where they previously weren't.
1 parent e6ffe35 commit d06d5ae

File tree

1 file changed

+48
-36
lines changed

1 file changed

+48
-36
lines changed

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

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,17 +1046,25 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
10461046
* - The nesting levels of A1 and A2 are the same, and A1's owner derives from A2's owner
10471047
* - A1's type is more specific than A2's type.
10481048
*/
1049-
def isAsGood(alt1: TermRef, alt2: TermRef, nesting1: Int = 0, nesting2: Int = 0)(implicit ctx: Context): Boolean = track("isAsGood") { trace(i"isAsGood($alt1, $alt2)", overload) {
1049+
def compare(alt1: TermRef, alt2: TermRef, nesting1: Int = 0, nesting2: Int = 0)(implicit ctx: Context): Int = track("compare") { trace(i"compare($alt1, $alt2)", overload) {
10501050

10511051
assert(alt1 ne alt2)
10521052

1053-
/** Is class or module class `sym1` derived from class or module class `sym2`?
1053+
/** Compare owner inheritance level.
1054+
* @param sym1 The first owner
1055+
* @param sym2 The second owner
1056+
* @return 1 if `sym1` properly derives from `sym2`
1057+
* -1 if `sym2` properly derives from `sym1`
1058+
* 0 otherwise
10541059
* Module classes also inherit the relationship from their companions.
10551060
*/
1056-
def isDerived(sym1: Symbol, sym2: Symbol): Boolean =
1057-
if (sym1 isSubClass sym2) true
1058-
else if (sym2 is Module) isDerived(sym1, sym2.companionClass)
1059-
else (sym1 is Module) && isDerived(sym1.companionClass, sym2)
1061+
def compareOwner(sym1: Symbol, sym2: Symbol): Int =
1062+
if (sym1 == sym2) 0
1063+
else if (sym1 isSubClass sym2) 1
1064+
else if (sym2 isSubClass sym1) -1
1065+
else if (sym2 is Module) compareOwner(sym1, sym2.companionClass)
1066+
else if (sym1 is Module) compareOwner(sym1.companionClass, sym2)
1067+
else 0
10601068

10611069
/** Is alternative `alt1` with type `tp1` as specific as alternative
10621070
* `alt2` with type `tp2` ?
@@ -1165,55 +1173,59 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
11651173

11661174
val owner1 = if (alt1.symbol.exists) alt1.symbol.owner else NoSymbol
11671175
val owner2 = if (alt2.symbol.exists) alt2.symbol.owner else NoSymbol
1176+
val ownerScore =
1177+
if (nesting1 > nesting2) 1
1178+
else if (nesting1 < nesting2) -1
1179+
else compareOwner(owner1, owner2)
1180+
11681181
val tp1 = stripImplicit(alt1.widen)
11691182
val tp2 = stripImplicit(alt2.widen)
1170-
1171-
def winsOwner1 =
1172-
nesting1 > nesting2 || nesting1 == nesting2 && isDerived(owner1, owner2)
11731183
def winsType1 = isAsSpecific(alt1, tp1, alt2, tp2)
1174-
def winsOwner2 =
1175-
nesting2 > nesting1 || nesting1 == nesting2 && isDerived(owner2, owner1)
11761184
def winsType2 = isAsSpecific(alt2, tp2, alt1, tp1)
11771185

1178-
overload.println(i"isAsGood($alt1, $alt2)? $tp1 $tp2 $winsOwner1 $winsType1 $winsOwner2 $winsType2")
1179-
1180-
// Assume the following probabilities:
1181-
//
1182-
// P(winsOwnerX) = 2/3
1183-
// P(winsTypeX) = 1/3
1184-
//
1185-
// Then the call probabilities of the 4 basic operations are as follows:
1186-
//
1187-
// winsOwner1: 1/1
1188-
// winsOwner2: 1/1
1189-
// winsType1 : 7/9
1190-
// winsType2 : 4/9
1191-
1192-
if (winsOwner1) /* 6/9 */ !winsOwner2 || /* 4/9 */ winsType1 || /* 8/27 */ !winsType2
1193-
else if (winsOwner2) /* 2/9 */ winsType1 && /* 2/27 */ !winsType2
1194-
else /* 1/9 */ winsType1 || /* 2/27 */ !winsType2
1186+
overload.println(i"compare($alt1, $alt2)? $tp1 $tp2 $ownerScore $winsType1 $winsType2")
1187+
1188+
if (ownerScore == 1)
1189+
if (winsType1 || !winsType2) 1 else 0
1190+
else if (ownerScore == -1)
1191+
if (winsType2 || !winsType1) -1 else 0
1192+
else if (winsType1)
1193+
if (winsType2) 0 else 1
1194+
else
1195+
if (winsType2) -1 else 0
11951196
}}
11961197

1198+
def isAsGood(alt1: TermRef, alt2: TermRef, nesting1: Int = 0, nesting2: Int = 0)(implicit ctx: Context): Boolean =
1199+
compare(alt1, alt2, nesting1, nesting2) >= 0
1200+
11971201
def narrowMostSpecific(alts: List[TermRef])(implicit ctx: Context): List[TermRef] = track("narrowMostSpecific") {
11981202
alts match {
11991203
case Nil => alts
12001204
case _ :: Nil => alts
1205+
case alt1 :: alt2 :: Nil =>
1206+
compare(alt1, alt2) match {
1207+
case 1 => alt1 :: Nil
1208+
case -1 => alt2 :: Nil
1209+
case 0 => alts
1210+
}
12011211
case alt :: alts1 =>
1202-
def winner(bestSoFar: TermRef, alts: List[TermRef]): TermRef = alts match {
1212+
def survivors(previous: List[TermRef], alts: List[TermRef]): List[TermRef] = alts match {
12031213
case alt :: alts1 =>
1204-
winner(if (isAsGood(alt, bestSoFar)) alt else bestSoFar, alts1)
1205-
case nil =>
1206-
bestSoFar
1214+
compare(previous.head, alt) match {
1215+
case 1 => survivors(previous, alts1)
1216+
case -1 => survivors(alt :: previous.tail, alts1)
1217+
case 0 => survivors(alt :: previous, alts1)
1218+
}
1219+
case Nil => previous
12071220
}
1208-
val best = winner(alt, alts1)
1221+
val best :: rest = survivors(alt :: Nil, alts1)
12091222
def asGood(alts: List[TermRef]): List[TermRef] = alts match {
12101223
case alt :: alts1 =>
1211-
if ((alt eq best) || !isAsGood(alt, best)) asGood(alts1)
1212-
else alt :: asGood(alts1)
1224+
if (compare(alt, best) < 0) asGood(alts1) else alt :: asGood(alts1)
12131225
case nil =>
12141226
Nil
12151227
}
1216-
best :: asGood(alts)
1228+
best :: asGood(rest)
12171229
}
12181230
}
12191231

0 commit comments

Comments
 (0)