Skip to content

Commit 693e81b

Browse files
authored
Merge pull request #9405 from dotty-staging/optimize-typecomparer
Simplify TypeComparer
2 parents 1821d22 + 7c64b9f commit 693e81b

31 files changed

+366
-224
lines changed

compiler/src/dotty/tools/dotc/Bench.scala

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,54 @@ import scala.annotation.internal.sharable
1010
* number of compilers and run each (sequentially) a given number of times
1111
* on the same sources.
1212
*/
13-
object Bench extends Driver {
13+
object Bench extends Driver:
1414

1515
@sharable private var numRuns = 1
1616

1717
private def ntimes(n: Int)(op: => Reporter): Reporter =
1818
(0 until n).foldLeft(emptyReporter)((_, _) => op)
1919

20+
@sharable private var times: Array[Int] = _
21+
2022
override def doCompile(compiler: Compiler, fileNames: List[String])(using Context): Reporter =
21-
ntimes(numRuns) {
23+
times = new Array[Int](numRuns)
24+
var reporter: Reporter = emptyReporter
25+
for i <- 0 until numRuns do
2226
val start = System.nanoTime()
23-
val r = super.doCompile(compiler, fileNames)
24-
println(s"time elapsed: ${(System.nanoTime - start) / 1000000}ms")
25-
if (ctx.settings.Xprompt.value) {
27+
reporter = super.doCompile(compiler, fileNames)
28+
times(i) = ((System.nanoTime - start) / 1000000).toInt
29+
println(s"time elapsed: ${times(i)}ms")
30+
if ctx.settings.Xprompt.value then
2631
print("hit <return> to continue >")
2732
System.in.read()
2833
println()
29-
}
30-
r
31-
}
34+
reporter
3235

3336
def extractNumArg(args: Array[String], name: String, default: Int = 1): (Int, Array[String]) = {
3437
val pos = args indexOf name
3538
if (pos < 0) (default, args)
3639
else (args(pos + 1).toInt, (args take pos) ++ (args drop (pos + 2)))
3740
}
3841

39-
override def process(args: Array[String], rootCtx: Context): Reporter = {
42+
def reportTimes() =
43+
val best = times.sorted
44+
val measured = numRuns / 3
45+
val avgBest = best.take(measured).sum / measured
46+
val avgLast = times.reverse.take(measured).sum / measured
47+
println(s"best out of $numRuns runs: ${best(0)}")
48+
println(s"average out of best $measured: $avgBest")
49+
println(s"average out of last $measured: $avgLast")
50+
51+
override def process(args: Array[String], rootCtx: Context): Reporter =
4052
val (numCompilers, args1) = extractNumArg(args, "#compilers")
4153
val (numRuns, args2) = extractNumArg(args1, "#runs")
4254
this.numRuns = numRuns
43-
ntimes(numCompilers)(super.process(args2, rootCtx))
44-
}
45-
}
55+
var reporter: Reporter = emptyReporter
56+
for i <- 0 until numCompilers do
57+
reporter = super.process(args2, rootCtx)
58+
reportTimes()
59+
reporter
60+
61+
end Bench
4662

4763

compiler/src/dotty/tools/dotc/config/Config.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ object Config {
4949
*/
5050
final val checkBackendNames = false
5151

52+
/** Check that re-used type comparers are in their initialization state */
53+
final val checkTypeComparerReset = false
54+
5255
/** Type comparer will fail with an assert if the upper bound
5356
* of a constrained parameter becomes Nothing. This should be turned
5457
* on only for specific debugging as normally instantiation to Nothing

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,18 @@ trait ConstraintHandling {
5454
*/
5555
protected var comparedTypeLambdas: Set[TypeLambda] = Set.empty
5656

57+
def checkReset() =
58+
assert(addConstraintInvocations == 0)
59+
assert(frozenConstraint == false)
60+
assert(caseLambda == NoType)
61+
assert(homogenizeArgs == false)
62+
assert(comparedTypeLambdas == Set.empty)
63+
5764
/** Gives for each instantiated type var that does not yet have its `inst` field
58-
* set, the instance value stored in the constraint. Storing instances in constraints
59-
* is done only in a temporary way for contexts that may be retracted
60-
* without also retracting the type var as a whole.
61-
*/
65+
* set, the instance value stored in the constraint. Storing instances in constraints
66+
* is done only in a temporary way for contexts that may be retracted
67+
* without also retracting the type var as a whole.
68+
*/
6269
def instType(tvar: TypeVar): Type = constraint.entry(tvar.origin) match {
6370
case _: TypeBounds => NoType
6471
case tp: TypeParamRef =>

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

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -172,17 +172,6 @@ object Contexts {
172172
protected def searchHistory_= (searchHistory: SearchHistory): Unit = _searchHistory = searchHistory
173173
final def searchHistory: SearchHistory = _searchHistory
174174

175-
/** The current type comparer. This ones updates itself automatically for
176-
* each new context.
177-
*/
178-
private var _typeComparer: TypeComparer = _
179-
protected def typeComparer_=(typeComparer: TypeComparer): Unit = _typeComparer = typeComparer
180-
def typeComparer: TypeComparer = {
181-
if (_typeComparer.comparerCtx ne this)
182-
_typeComparer = _typeComparer.copyIn(this)
183-
_typeComparer
184-
}
185-
186175
/** The current source file */
187176
private var _source: SourceFile = _
188177
protected def source_=(source: SourceFile): Unit = _source = source
@@ -479,7 +468,6 @@ object Contexts {
479468
_typeAssigner = origin.typeAssigner
480469
_gadt = origin.gadt
481470
_searchHistory = origin.searchHistory
482-
_typeComparer = origin.typeComparer
483471
_source = origin.source
484472
_moreProperties = origin.moreProperties
485473
_store = origin.store
@@ -610,7 +598,6 @@ object Contexts {
610598
util.Stats.record("Context.setSource")
611599
this.source = source
612600
this
613-
def setTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this }
614601
private def setMoreProperties(moreProperties: Map[Key[Any], Any]): this.type =
615602
util.Stats.record("Context.setMoreProperties")
616603
this.moreProperties = moreProperties
@@ -699,26 +686,57 @@ object Contexts {
699686
val base = ctx.base
700687
import base._
701688
val nestedCtx =
702-
if testsInUse < testContexts.size then
703-
testContexts(testsInUse).reuseIn(ctx)
689+
if exploresInUse < exploreContexts.size then
690+
exploreContexts(exploresInUse).reuseIn(ctx)
704691
else
705692
val ts = TyperState()
706693
.setReporter(ExploringReporter())
707694
.setCommittable(false)
708695
val c = FreshContext(ctx.base).init(ctx, ctx).setTyperState(ts)
709-
testContexts += c
696+
exploreContexts += c
710697
c
711-
testsInUse += 1
698+
exploresInUse += 1
712699
val nestedTS = nestedCtx.typerState
713700
nestedTS.init(ctx.typerState, ctx.typerState.constraint)
714701
val result =
715702
try op(using nestedCtx)
716703
finally
717704
nestedTS.reporter.asInstanceOf[ExploringReporter].reset()
718-
testsInUse -= 1
705+
exploresInUse -= 1
719706
result
720707
end explore
721708

709+
/** The type comparer of the kind created by `maker` to be used.
710+
* This is the currently active type comparer CMP if
711+
* - CMP is associated with the current context, and
712+
* - CMP is of the kind created by maker or maker creates a plain type comparer.
713+
* Note: plain TypeComparers always take on the kind of the outer comparer if they are in the same context.
714+
* In other words: tracking or explaining is a sticky property in the same context.
715+
*/
716+
private def comparer(using Context): TypeComparer =
717+
val base = ctx.base
718+
if base.comparersInUse > 0
719+
&& (base.comparers(base.comparersInUse - 1).comparerContext eq ctx)
720+
then
721+
base.comparers(base.comparersInUse - 1).currentInstance
722+
else
723+
val result =
724+
if base.comparersInUse < base.comparers.size then
725+
base.comparers(base.comparersInUse)
726+
else
727+
val result = TypeComparer(ctx)
728+
base.comparers += result
729+
result
730+
base.comparersInUse += 1
731+
result.init(ctx)
732+
result
733+
734+
inline def comparing[T](inline op: TypeComparer => T)(using Context): T =
735+
val saved = ctx.base.comparersInUse
736+
try op(comparer)
737+
finally ctx.base.comparersInUse = saved
738+
end comparing
739+
722740
/** A class defining the initial context with given context base
723741
* and set of possible settings.
724742
*/
@@ -735,7 +753,6 @@ object Contexts {
735753
store = initialStore
736754
.updated(settingsStateLoc, settingsGroup.defaultState)
737755
.updated(notNullInfosLoc, Nil)
738-
typeComparer = new TypeComparer(using this)
739756
searchHistory = new SearchRoot
740757
gadt = EmptyGadtConstraint
741758
}
@@ -871,14 +888,18 @@ object Contexts {
871888

872889
protected[dotc] val indentTab: String = " "
873890

874-
private[dotc] val testContexts = new mutable.ArrayBuffer[FreshContext]
875-
private[dotc] var testsInUse: Int = 0
891+
private[Contexts] val exploreContexts = new mutable.ArrayBuffer[FreshContext]
892+
private[Contexts] var exploresInUse: Int = 0
893+
894+
private[Contexts] val comparers = new mutable.ArrayBuffer[TypeComparer]
895+
private[Contexts] var comparersInUse: Int = 0
876896

877897
def reset(): Unit = {
878898
for ((_, set) <- uniqueSets) set.clear()
879899
errorTypeMsg.clear()
880900
sources.clear()
881901
sourceNamed.clear()
902+
comparers.clear() // forces re-evaluation of top and bottom classes in TypeComparer
882903
}
883904

884905
// Test that access is single threaded

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ class Definitions {
13241324
def asContextFunctionType(tp: Type)(using Context): Type =
13251325
tp.stripTypeVar.dealias match {
13261326
case tp1: TypeParamRef if ctx.typerState.constraint.contains(tp1) =>
1327-
asContextFunctionType(ctx.typeComparer.bounds(tp1).hiBound)
1327+
asContextFunctionType(TypeComparer.bounds(tp1).hiBound)
13281328
case tp1 =>
13291329
if (isFunctionType(tp1) && tp1.typeSymbol.name.isContextFunction) tp1
13301330
else NoType

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ object Denotations {
539539
case tp1: MethodType =>
540540
tp2 match
541541
case tp2: MethodType
542-
if ctx.typeComparer.matchingMethodParams(tp1, tp2)
542+
if TypeComparer.matchingMethodParams(tp1, tp2)
543543
&& tp1.isImplicitMethod == tp2.isImplicitMethod
544544
&& tp1.isErasedMethod == tp2.isErasedMethod =>
545545
val resType = infoMeet(tp1.resType, tp2.resType.subst(tp2, tp1), safeIntersection)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ final class ProperGadtConstraint private(
220220
override protected def constraint = myConstraint
221221
override protected def constraint_=(c: Constraint) = myConstraint = c
222222

223-
override protected def isSub(tp1: Type, tp2: Type)(using Context): Boolean = ctx.typeComparer.isSubType(tp1, tp2)
224-
override protected def isSame(tp1: Type, tp2: Type)(using Context): Boolean = ctx.typeComparer.isSameType(tp1, tp2)
223+
override protected def isSub(tp1: Type, tp2: Type)(using Context): Boolean = TypeComparer.isSubType(tp1, tp2)
224+
override protected def isSame(tp1: Type, tp2: Type)(using Context): Boolean = TypeComparer.isSameType(tp1, tp2)
225225

226226
override def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds =
227227
constraint.nonParamBounds(param) match {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1991,7 +1991,7 @@ object SymDenotations {
19911991
computeApplied
19921992

19931993
case tp: TypeParamRef => // uncachable, since baseType depends on context bounds
1994-
recur(ctx.typeComparer.bounds(tp).hi)
1994+
recur(TypeComparer.bounds(tp).hi)
19951995

19961996
case tp: TypeProxy =>
19971997
def computeTypeProxy = {

0 commit comments

Comments
 (0)