Skip to content

Simplify TypeComparer #9405

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 28 additions & 12 deletions compiler/src/dotty/tools/dotc/Bench.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,54 @@ import scala.annotation.internal.sharable
* number of compilers and run each (sequentially) a given number of times
* on the same sources.
*/
object Bench extends Driver {
object Bench extends Driver:

@sharable private var numRuns = 1

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

@sharable private var times: Array[Int] = _

override def doCompile(compiler: Compiler, fileNames: List[String])(using Context): Reporter =
ntimes(numRuns) {
times = new Array[Int](numRuns)
var reporter: Reporter = emptyReporter
for i <- 0 until numRuns do
val start = System.nanoTime()
val r = super.doCompile(compiler, fileNames)
println(s"time elapsed: ${(System.nanoTime - start) / 1000000}ms")
if (ctx.settings.Xprompt.value) {
reporter = super.doCompile(compiler, fileNames)
times(i) = ((System.nanoTime - start) / 1000000).toInt
println(s"time elapsed: ${times(i)}ms")
if ctx.settings.Xprompt.value then
print("hit <return> to continue >")
System.in.read()
println()
}
r
}
reporter

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

override def process(args: Array[String], rootCtx: Context): Reporter = {
def reportTimes() =
val best = times.sorted
val measured = numRuns / 3
val avgBest = best.take(measured).sum / measured
val avgLast = times.reverse.take(measured).sum / measured
println(s"best out of $numRuns runs: ${best(0)}")
println(s"average out of best $measured: $avgBest")
println(s"average out of last $measured: $avgLast")

override def process(args: Array[String], rootCtx: Context): Reporter =
val (numCompilers, args1) = extractNumArg(args, "#compilers")
val (numRuns, args2) = extractNumArg(args1, "#runs")
this.numRuns = numRuns
ntimes(numCompilers)(super.process(args2, rootCtx))
}
}
var reporter: Reporter = emptyReporter
for i <- 0 until numCompilers do
reporter = super.process(args2, rootCtx)
reportTimes()
reporter

end Bench


3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ object Config {
*/
final val checkBackendNames = false

/** Check that re-used type comparers are in their initialization state */
final val checkTypeComparerReset = false

/** Type comparer will fail with an assert if the upper bound
* of a constrained parameter becomes Nothing. This should be turned
* on only for specific debugging as normally instantiation to Nothing
Expand Down
15 changes: 11 additions & 4 deletions compiler/src/dotty/tools/dotc/core/ConstraintHandling.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,18 @@ trait ConstraintHandling {
*/
protected var comparedTypeLambdas: Set[TypeLambda] = Set.empty

def checkReset() =
assert(addConstraintInvocations == 0)
assert(frozenConstraint == false)
assert(caseLambda == NoType)
assert(homogenizeArgs == false)
assert(comparedTypeLambdas == Set.empty)

/** Gives for each instantiated type var that does not yet have its `inst` field
* set, the instance value stored in the constraint. Storing instances in constraints
* is done only in a temporary way for contexts that may be retracted
* without also retracting the type var as a whole.
*/
* set, the instance value stored in the constraint. Storing instances in constraints
* is done only in a temporary way for contexts that may be retracted
* without also retracting the type var as a whole.
*/
def instType(tvar: TypeVar): Type = constraint.entry(tvar.origin) match {
case _: TypeBounds => NoType
case tp: TypeParamRef =>
Expand Down
63 changes: 42 additions & 21 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,17 +172,6 @@ object Contexts {
protected def searchHistory_= (searchHistory: SearchHistory): Unit = _searchHistory = searchHistory
final def searchHistory: SearchHistory = _searchHistory

/** The current type comparer. This ones updates itself automatically for
* each new context.
*/
private var _typeComparer: TypeComparer = _
protected def typeComparer_=(typeComparer: TypeComparer): Unit = _typeComparer = typeComparer
def typeComparer: TypeComparer = {
if (_typeComparer.comparerCtx ne this)
_typeComparer = _typeComparer.copyIn(this)
_typeComparer
}

/** The current source file */
private var _source: SourceFile = _
protected def source_=(source: SourceFile): Unit = _source = source
Expand Down Expand Up @@ -479,7 +468,6 @@ object Contexts {
_typeAssigner = origin.typeAssigner
_gadt = origin.gadt
_searchHistory = origin.searchHistory
_typeComparer = origin.typeComparer
_source = origin.source
_moreProperties = origin.moreProperties
_store = origin.store
Expand Down Expand Up @@ -610,7 +598,6 @@ object Contexts {
util.Stats.record("Context.setSource")
this.source = source
this
def setTypeComparerFn(tcfn: Context => TypeComparer): this.type = { this.typeComparer = tcfn(this); this }
private def setMoreProperties(moreProperties: Map[Key[Any], Any]): this.type =
util.Stats.record("Context.setMoreProperties")
this.moreProperties = moreProperties
Expand Down Expand Up @@ -699,26 +686,57 @@ object Contexts {
val base = ctx.base
import base._
val nestedCtx =
if testsInUse < testContexts.size then
testContexts(testsInUse).reuseIn(ctx)
if exploresInUse < exploreContexts.size then
exploreContexts(exploresInUse).reuseIn(ctx)
else
val ts = TyperState()
.setReporter(ExploringReporter())
.setCommittable(false)
val c = FreshContext(ctx.base).init(ctx, ctx).setTyperState(ts)
testContexts += c
exploreContexts += c
c
testsInUse += 1
exploresInUse += 1
val nestedTS = nestedCtx.typerState
nestedTS.init(ctx.typerState, ctx.typerState.constraint)
val result =
try op(using nestedCtx)
finally
nestedTS.reporter.asInstanceOf[ExploringReporter].reset()
testsInUse -= 1
exploresInUse -= 1
result
end explore

/** The type comparer of the kind created by `maker` to be used.
* This is the currently active type comparer CMP if
* - CMP is associated with the current context, and
* - CMP is of the kind created by maker or maker creates a plain type comparer.
* Note: plain TypeComparers always take on the kind of the outer comparer if they are in the same context.
* In other words: tracking or explaining is a sticky property in the same context.
*/
private def comparer(using Context): TypeComparer =
val base = ctx.base
if base.comparersInUse > 0
&& (base.comparers(base.comparersInUse - 1).comparerContext eq ctx)
then
base.comparers(base.comparersInUse - 1).currentInstance
else
val result =
if base.comparersInUse < base.comparers.size then
base.comparers(base.comparersInUse)
else
val result = TypeComparer(ctx)
base.comparers += result
result
base.comparersInUse += 1
result.init(ctx)
result

inline def comparing[T](inline op: TypeComparer => T)(using Context): T =
val saved = ctx.base.comparersInUse
try op(comparer)
finally ctx.base.comparersInUse = saved
end comparing

/** A class defining the initial context with given context base
* and set of possible settings.
*/
Expand All @@ -735,7 +753,6 @@ object Contexts {
store = initialStore
.updated(settingsStateLoc, settingsGroup.defaultState)
.updated(notNullInfosLoc, Nil)
typeComparer = new TypeComparer(using this)
searchHistory = new SearchRoot
gadt = EmptyGadtConstraint
}
Expand Down Expand Up @@ -871,14 +888,18 @@ object Contexts {

protected[dotc] val indentTab: String = " "

private[dotc] val testContexts = new mutable.ArrayBuffer[FreshContext]
private[dotc] var testsInUse: Int = 0
private[Contexts] val exploreContexts = new mutable.ArrayBuffer[FreshContext]
private[Contexts] var exploresInUse: Int = 0

private[Contexts] val comparers = new mutable.ArrayBuffer[TypeComparer]
private[Contexts] var comparersInUse: Int = 0

def reset(): Unit = {
for ((_, set) <- uniqueSets) set.clear()
errorTypeMsg.clear()
sources.clear()
sourceNamed.clear()
comparers.clear() // forces re-evaluation of top and bottom classes in TypeComparer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to clear exploreContexts, and reset exploresInUse and comparersInUse to 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, every nested call with reset to the previous state after itself, even under exceptions. So there's nothing to do here.

}

// Test that access is single threaded
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1324,7 +1324,7 @@ class Definitions {
def asContextFunctionType(tp: Type)(using Context): Type =
tp.stripTypeVar.dealias match {
case tp1: TypeParamRef if ctx.typerState.constraint.contains(tp1) =>
asContextFunctionType(ctx.typeComparer.bounds(tp1).hiBound)
asContextFunctionType(TypeComparer.bounds(tp1).hiBound)
case tp1 =>
if (isFunctionType(tp1) && tp1.typeSymbol.name.isContextFunction) tp1
else NoType
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ object Denotations {
case tp1: MethodType =>
tp2 match
case tp2: MethodType
if ctx.typeComparer.matchingMethodParams(tp1, tp2)
if TypeComparer.matchingMethodParams(tp1, tp2)
&& tp1.isImplicitMethod == tp2.isImplicitMethod
&& tp1.isErasedMethod == tp2.isErasedMethod =>
val resType = infoMeet(tp1.resType, tp2.resType.subst(tp2, tp1), safeIntersection)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/GadtConstraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,8 @@ final class ProperGadtConstraint private(
override protected def constraint = myConstraint
override protected def constraint_=(c: Constraint) = myConstraint = c

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

override def nonParamBounds(param: TypeParamRef)(using Context): TypeBounds =
constraint.nonParamBounds(param) match {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1991,7 +1991,7 @@ object SymDenotations {
computeApplied

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

case tp: TypeProxy =>
def computeTypeProxy = {
Expand Down
Loading