From afbe02d982c4979adfcd17cb59d71d5affb511ed Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Apr 2021 16:26:07 +0200 Subject: [PATCH 1/4] Peformance test for WIP Use these changes to compile the stdlib. You need to comment out AnyVal.scala and then use the following command on community-projects/stdLib213: sc -Ydetailed-stats *.scala */*.scala */*/*.scala */*/*/*.scala What I see is: Calls to `translucentSuperType` 53152: try norm KO -> 53086 try norm OK -> 66 The only computationally relevant part of this are calls to tryMatchAlias try match alias -> 528 Those calls suceed in 66 cases try norm OK -> 66 The successful normalizations all return types like this: try norm OK F[T1] *: scala.Tuple.Map[(T2, T3, T4, T5, T6, T7, T8), F] -> 3 Now it could be that trying the normalizations is costly, or that the normalized types cause exhaustivity checking to explode. Tht remains to be found out. --- compiler/src/dotty/tools/dotc/core/Types.scala | 12 +++++++++++- compiler/src/dotty/tools/dotc/util/Stats.scala | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8a0b0f12c167..49abfbce5957 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4019,7 +4019,14 @@ object Types { case tycon: TypeRef if tycon.symbol.isOpaqueAlias => tycon.translucentSuperType.applyIfParameterized(args) case _ => - tryNormalize.orElse(superType) + val x = tryNormalize + if x.exists then + record("try norm OK") + record(i"try norm OK $x") + x + else + record("try norm KO") + superType } inline def map(inline op: Type => Type)(using Context) = @@ -4038,6 +4045,9 @@ object Types { case tycon: TypeRef => def tryMatchAlias = tycon.info match { case MatchAlias(alias) => + record(i"try match alias") + record(i"try match alias $this in ${ctx.owner.ownersIterator.toList}%, %") + //new Error().printStackTrace() trace(i"normalize $this", typr, show = true) { MatchTypeTrace.recurseWith(this) { alias.applyIfParameterized(args).tryNormalize diff --git a/compiler/src/dotty/tools/dotc/util/Stats.scala b/compiler/src/dotty/tools/dotc/util/Stats.scala index 684083fa77ba..926c87fac1df 100644 --- a/compiler/src/dotty/tools/dotc/util/Stats.scala +++ b/compiler/src/dotty/tools/dotc/util/Stats.scala @@ -9,7 +9,7 @@ import collection.mutable @sharable object Stats { - final val enabled = false + final val enabled = true var monitored: Boolean = false From 86d56f963233945c908c55919a3306285f4014fd Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 3 May 2021 10:59:04 +0200 Subject: [PATCH 2/4] Fix #12267: Constraints should not cross phases To enforce the invariant, we need to ensure that tvars are instantiated at end of phases. --- bench/profiles/stdlib.yml | 15 +++++++++++++++ compiler/src/dotty/tools/dotc/Run.scala | 9 +++++++++ compiler/src/dotty/tools/dotc/util/Stats.scala | 2 +- tests/pos/t2619b.scala | 8 ++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 bench/profiles/stdlib.yml create mode 100644 tests/pos/t2619b.scala diff --git a/bench/profiles/stdlib.yml b/bench/profiles/stdlib.yml new file mode 100644 index 000000000000..bffcd5b86968 --- /dev/null +++ b/bench/profiles/stdlib.yml @@ -0,0 +1,15 @@ +charts: + + - name: "scala stdlib-2.13" + url: https://github.com/dotty-staging/scala/commits/stdLib213-dotty-community-build + lines: + - key: stdlib213 + label: bootstrapped + +scripts: + + stdlib213: + - source $PROG_HOME/dotty/bench/scripts/stdlib213 + +config: + pr_base_url: "https://github.com/lampepfl/dotty/pull/" diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 6b51908c37d7..737b7af93882 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -202,7 +202,16 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint Stats.trackTime(s"$phase ms ") { val start = System.currentTimeMillis val profileBefore = profiler.beforePhase(phase) + + // invariant: constraint should not cross phase boundary + ctx.typerState.constraint = OrderingConstraint.empty + units = phase.runOn(units) + + // force instantiate tvars + // see tests/pos/t2619b.scala + ctx.typerState.gc() + profiler.afterPhase(phase, profileBefore) if (ctx.settings.Xprint.value.containsPhase(phase)) for (unit <- units) diff --git a/compiler/src/dotty/tools/dotc/util/Stats.scala b/compiler/src/dotty/tools/dotc/util/Stats.scala index 926c87fac1df..684083fa77ba 100644 --- a/compiler/src/dotty/tools/dotc/util/Stats.scala +++ b/compiler/src/dotty/tools/dotc/util/Stats.scala @@ -9,7 +9,7 @@ import collection.mutable @sharable object Stats { - final val enabled = true + final val enabled = false var monitored: Boolean = false diff --git a/tests/pos/t2619b.scala b/tests/pos/t2619b.scala new file mode 100644 index 000000000000..e1519669a52a --- /dev/null +++ b/tests/pos/t2619b.scala @@ -0,0 +1,8 @@ +abstract class AbstractModule + +object ModuleBE extends AbstractModule +object ModuleBF extends AbstractModule + +object ModuleBM extends AbstractModule { + def ms: List[AbstractModule] = List(ModuleBE) ::: List(ModuleBF) +} From 553f3cd8653892b23989d361308e281a4738d4c5 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 3 May 2021 21:30:11 +0200 Subject: [PATCH 3/4] Code refactoring --- compiler/src/dotty/tools/dotc/Run.scala | 8 +------- compiler/src/dotty/tools/dotc/core/TyperState.scala | 13 +++++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index 737b7af93882..bc9e0b490c2b 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -204,13 +204,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint val profileBefore = profiler.beforePhase(phase) // invariant: constraint should not cross phase boundary - ctx.typerState.constraint = OrderingConstraint.empty - - units = phase.runOn(units) - - // force instantiate tvars - // see tests/pos/t2619b.scala - ctx.typerState.gc() + ctx.typerState.ensureClosedConstraint { units = phase.runOn(units) } profiler.afterPhase(phase, profileBefore) if (ctx.settings.Xprint.value.containsPhase(phase)) diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 8b4b6a476d1b..717d78d86726 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -159,5 +159,18 @@ class TyperState() { s"TS[${ids(this).mkString(", ")}]" } + /** Execute the operation with an empty constraint and make sure no leak + * of constraints. + * + * Side effect: the method will reset the constraint associated with the context. + */ + def ensureClosedConstraint[T](op: => T)(using Context): T = + this.constraint = OrderingConstraint.empty + val res = op + // force instantiate tvars + // see tests/pos/t2619b.scala + ctx.typerState.gc() + res + def stateChainStr: String = s"$this${if (previous == null) "" else previous.stateChainStr}" } From 660ba6440443daa5e0527d7fc67139627766f955 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 4 May 2021 09:23:44 +0200 Subject: [PATCH 4/4] Rename ensureClosedConstraint to ensureSegregated --- compiler/src/dotty/tools/dotc/Run.scala | 2 +- compiler/src/dotty/tools/dotc/core/TyperState.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Run.scala b/compiler/src/dotty/tools/dotc/Run.scala index bc9e0b490c2b..e143ea5891ab 100644 --- a/compiler/src/dotty/tools/dotc/Run.scala +++ b/compiler/src/dotty/tools/dotc/Run.scala @@ -204,7 +204,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint val profileBefore = profiler.beforePhase(phase) // invariant: constraint should not cross phase boundary - ctx.typerState.ensureClosedConstraint { units = phase.runOn(units) } + ctx.typerState.ensureSegregated { units = phase.runOn(units) } profiler.afterPhase(phase, profileBefore) if (ctx.settings.Xprint.value.containsPhase(phase)) diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 717d78d86726..4b8751a38b5e 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -164,7 +164,7 @@ class TyperState() { * * Side effect: the method will reset the constraint associated with the context. */ - def ensureClosedConstraint[T](op: => T)(using Context): T = + def ensureSegregated[T](op: => T)(using Context): T = this.constraint = OrderingConstraint.empty val res = op // force instantiate tvars