Skip to content

Re-use contexts when changing owners in MegaPhase #9659

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 1 commit into from
Sep 1, 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
89 changes: 48 additions & 41 deletions compiler/src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,36 @@ package dotty.tools.dotc.config

object Config {

final val cacheMembersNamed = true
final val cacheAsSeenFrom = true
final val cacheMemberNames = true
final val cacheImplicitScopes = true
final val cacheMatchReduced = true
inline val cacheMembersNamed = true
inline val cacheAsSeenFrom = true
inline val cacheMemberNames = true
inline val cacheImplicitScopes = true
inline val cacheMatchReduced = true

final val checkCacheMembersNamed = false
/** If true, the `runWithOwner` operation uses a re-usable context,
* similar to explore. This requires that the context does not escape
* the call. If false, `runWithOwner` runs its operation argument
* in a fresh context.
*/
inline val reuseOwnerContexts = true

inline val checkCacheMembersNamed = false

/** When updating a constraint bound, check that the constrained parameter
* does not appear at the top-level of either of its bounds.
*/
final val checkConstraintsNonCyclic = false
inline val checkConstraintsNonCyclic = false

/** Check that each constraint resulting from a subtype test
* is satisfiable.
*/
final val checkConstraintsSatisfiable = false
inline val checkConstraintsSatisfiable = false

/** Check that each constraint is fully propagated. i.e.
* If P <: Q then the upper bound of P is a subtype of the upper bound of Q
* and the lower bound of Q is a subtype of the lower bound of P.
*/
final val checkConstraintsPropagated = false
inline val checkConstraintsPropagated = false

/** Check that constraints of globally committable typer states are closed.
* NOTE: When enabled, the check can cause CyclicReference errors because
Expand All @@ -35,51 +42,51 @@ object Config {
* It is recommended to turn this option on only when chasing down
* a TypeParamRef instantiation error. See comment in Types.TypeVar.instantiate.
*/
final val debugCheckConstraintsClosed = false
inline val debugCheckConstraintsClosed = false

/** Check that no type appearing as the info of a SymDenotation contains
* skolem types.
*/
final val checkNoSkolemsInInfo = false
inline val checkNoSkolemsInInfo = false

/** Check that Name#toString is not called directly from backend by analyzing
* the stack trace of each toString call on names. This is very expensive,
* so not suitable for continuous testing. But it can be used to find a problem
* when running a specific test.
*/
final val checkBackendNames = false
inline val checkBackendNames = false

/** Check that re-used type comparers are in their initialization state */
final val checkTypeComparerReset = false
inline 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
* is not an error condition.
*/
final val failOnInstantiationToNothing = false
inline val failOnInstantiationToNothing = false

/** Enable noDoubleDef checking if option "-YnoDoubleDefs" is set.
* The reason to have an option as well as the present global switch is
* that the noDoubleDef checking is done in a hotspot, and we do not
* want to incur the overhead of checking an option each time.
*/
final val checkNoDoubleBindings = true
inline val checkNoDoubleBindings = true

/** Check positions for consistency after parsing */
final val checkPositions = true
inline val checkPositions = true

/** Check that typed trees don't point to untyped ones */
final val checkTreesConsistent = false
inline val checkTreesConsistent = false

/** Show subtype traces for all deep subtype recursions */
final val traceDeepSubTypeRecursions = false
inline val traceDeepSubTypeRecursions = false

/** When explaining subtypes and this flag is set, also show the classes of the compared types. */
final val verboseExplainSubtype = false
inline val verboseExplainSubtype = false

/** If this flag is set, take the fast path when comparing same-named type-aliases and types */
final val fastPathForRefinedSubtype = true
inline val fastPathForRefinedSubtype = true

/** If this flag is set, and we compute `T1[X1]` & `T2[X2]` as a new
* upper bound of a constrained parameter, try to align the arguments by computing
Expand All @@ -88,20 +95,20 @@ object Config {
*
* For more info, see the comment in `TypeComparer#glbArgs`.
*/
final val alignArgsInAnd = true
inline val alignArgsInAnd = true

/** If this flag is set, higher-kinded applications are checked for validity
*/
final val checkHKApplications = false
inline val checkHKApplications = false

/** If this flag is set, method types are checked for valid parameter references
*/
final val checkMethodTypes = false
inline val checkMethodTypes = false

/** If this flag is set, it is checked that TypeRefs don't refer directly
* to themselves.
*/
final val checkTypeRefCycles = false
inline val checkTypeRefCycles = false

/** If this flag is set, we check that types assigned to trees are error types only
* if some error was already reported. There are complicicated scenarios where this
Expand All @@ -113,35 +120,35 @@ object Config {
* this in all circumstances. But since it is almost always true it is useful to
* keep the Config option for debugging.
*/
final val checkUnreportedErrors = false
inline val checkUnreportedErrors = false

/** If this flag is set, it is checked that class type parameters are
* only references with NoPrefix or ThisTypes as prefixes. This option
* is usually disabled, because there are still some legitimate cases where
* this can arise (e.g. for pos/Map.scala, in LambdaType.integrate).
*/
final val checkTypeParamRefs = false
inline val checkTypeParamRefs = false

/** The recursion depth for showing a summarized string */
final val summarizeDepth = 2
inline val summarizeDepth = 2

/** Check that variances of lambda arguments match the
* variance of the underlying lambda class.
*/
final val checkLambdaVariance = false
inline val checkLambdaVariance = false

/** Check that certain types cannot be created in erasedTypes phases.
* Note: Turning this option on will get some false negatives, since it is
* possible that And/Or types are still created during erasure as the result
* of some operation on an existing type.
*/
final val checkUnerased = false
inline val checkUnerased = false

/** Check that atoms-based comparisons match regular comparisons that do not
* take atoms into account. The two have to give the same results, since
* atoms comparison is intended to be just an optimization.
*/
final val checkAtomsComparisons = false
inline val checkAtomsComparisons = false

/** In `derivedSelect`, rewrite
*
Expand All @@ -151,39 +158,39 @@ object Config {
* Not sure whether this is useful. Preliminary measurements show a slowdown of about
* 7% for the build when this option is enabled.
*/
final val splitProjections = false
inline val splitProjections = false

/** If this flag is on, always rewrite an application `S[Ts]` where `S` is an alias for
* `[Xs] -> U` to `[Xs := Ts]U`.
* Turning this flag on was observed to give a ~6% speedup on the JUnit test suite.
*/
final val simplifyApplications = true
inline val simplifyApplications = true

/** Assume -indent by default */
final val defaultIndent = true
inline val defaultIndent = true

/** If set, prints a trace of all symbol completions */
final val showCompletions = false
inline val showCompletions = false

/** If set, method results that are context functions are flattened by adding
* the parameters of the context function results to the methods themselves.
* This is an optimization that reduces closure allocations.
*/
final val flattenContextFunctionResults = true
inline val flattenContextFunctionResults = true

/** If set, enables tracing */
final val tracingEnabled = false
inline val tracingEnabled = false

/** Initial capacity of uniques HashMap.
* Note: This MUST BE a power of two to work with util.HashSet
*/
final val initialUniquesCapacity = 65536
inline val initialUniquesCapacity = 65536

/** How many recursive calls to NamedType#underlying are performed before logging starts. */
final val LogPendingUnderlyingThreshold = 50
inline val LogPendingUnderlyingThreshold = 50

/** How many recursive calls to isSubType are performed before logging starts. */
final val LogPendingSubTypesThreshold = 50
inline val LogPendingSubTypesThreshold = 50

/** How many recursive calls to findMember are performed before logging names starts
* Note: this threshold has to be chosen carefully. Too large, and programs
Expand All @@ -194,8 +201,8 @@ object Config {
* dotty itself only causes small pending names lists to be generated (we measured
* at max 6 elements) and these lists are never searched with contains.
*/
final val LogPendingFindMemberThreshold = 9
inline val LogPendingFindMemberThreshold = 9

/** When in IDE, turn StaleSymbol errors into warnings instead of crashing */
final val ignoreStaleInIDE = true
inline val ignoreStaleInIDE = true
}
25 changes: 25 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,28 @@ object Contexts {
val ectx = exploreCtx
try op(using ectx) finally wrapUpExplore(ectx)

private def changeOwnerCtx(owner: Symbol)(using Context): Context =
val base = ctx.base
import base._
val nestedCtx =
if changeOwnersInUse < changeOwnerContexts.size then
changeOwnerContexts(changeOwnersInUse).reuseIn(ctx)
else
val c = FreshContext(ctx.base).init(ctx, ctx)
changeOwnerContexts += c
c
changeOwnersInUse += 1
nestedCtx.setOwner(owner).setTyperState(ctx.typerState)

/** Run `op` in current context, with a mode is temporarily set as specified.
*/
inline def runWithOwner[T](owner: Symbol)(inline op: Context ?=> T)(using Context): T =
if Config.reuseOwnerContexts then
try op(using changeOwnerCtx(owner))
finally ctx.base.changeOwnersInUse -= 1
else
op(using ctx.fresh.setOwner(owner))

/** 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
Expand Down Expand Up @@ -887,6 +909,9 @@ object Contexts {
private[Contexts] val exploreContexts = new mutable.ArrayBuffer[FreshContext]
private[Contexts] var exploresInUse: Int = 0

private[Contexts] val changeOwnerContexts = new mutable.ArrayBuffer[FreshContext]
private[Contexts] var changeOwnersInUse: Int = 0

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

Expand Down
19 changes: 10 additions & 9 deletions compiler/src/dotty/tools/dotc/transform/MegaPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,10 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {

/** Transform full tree using all phases in this group that have idxInGroup >= start */
def transformTree(tree: Tree, start: Int)(using Context): Tree = {
def localContext(using Context) = {

inline def inLocalContext[T](inline op: Context ?=> T)(using Context): T =
val sym = tree.symbol
val owner = if (sym.is(PackageVal)) sym.moduleClass else sym
ctx.fresh.setOwner(owner)
}
runWithOwner(if (sym.is(PackageVal)) sym.moduleClass else sym)(op)

def transformNamed(tree: Tree, start: Int, outerCtx: Context): Tree = tree match {
case tree: Ident =>
Expand All @@ -236,8 +235,10 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
val rhs = transformTree(tree.rhs, start)
cpy.ValDef(tree)(tree.name, tpt, rhs)
}
if (tree.isEmpty) tree
else goValDef(mapValDef(using if (tree.symbol.exists) localContext else ctx), start)
if tree.isEmpty then tree
else goValDef(
if tree.symbol.exists then inLocalContext(mapValDef) else mapValDef,
start)
}
case tree: DefDef =>
inContext(prepDefDef(tree, start)(using outerCtx)) {
Expand All @@ -248,11 +249,11 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
val rhs = transformTree(tree.rhs, start)
cpy.DefDef(tree)(tree.name, tparams, vparamss, tpt, rhs)
}
goDefDef(mapDefDef(using localContext), start)
goDefDef(inLocalContext(mapDefDef), start)
}
case tree: TypeDef =>
inContext(prepTypeDef(tree, start)(using outerCtx)) {
val rhs = transformTree(tree.rhs, start)(using localContext)
val rhs = inLocalContext(transformTree(tree.rhs, start))
goTypeDef(cpy.TypeDef(tree)(tree.name, rhs), start)
}
case tree: Labeled =>
Expand Down Expand Up @@ -381,7 +382,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
val stats = transformStats(tree.stats, tree.symbol, start)
cpy.PackageDef(tree)(pid, stats)
}
goPackageDef(mapPackage(using localContext), start)
goPackageDef(inLocalContext(mapPackage), start)
}
case tree: Try =>
inContext(prepTry(tree, start)(using outerCtx)) {
Expand Down