diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index cbdc40996a91..19724f28698f 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -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 @@ -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 @@ -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 @@ -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 * @@ -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 @@ -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 } diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 95875bfedcc1..efdbc155a09a 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -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 @@ -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 diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index f9aa04fe6c09..385d02638b9f 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -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 => @@ -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)) { @@ -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 => @@ -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)) {