From bced61d0bb58e8e4da1c7e8ececb1c24b5e81843 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Nov 2015 17:25:22 +0100 Subject: [PATCH 1/5] The change to do compareAlias early caused a dramatic slowdown of compilation compileStdLib went from 45 sec to 230 sec. The problem were many redundant tests when every member of an alias chain was compared to every other. The new scheme follows alias chains to their end before doing anything else. --- src/dotty/tools/dotc/core/TypeComparer.scala | 56 ++++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 0d46a4b9067f..d94e244694b0 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -140,35 +140,35 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private def firstTry(tp1: Type, tp2: Type): Boolean = tp2 match { case tp2: NamedType => - def compareAlias(info1: Type) = tp2.info match { - case info2: TypeAlias => isSubType(tp1, info2.alias) - case _ => info1 match { - case info1: TypeAlias => isSubType(info1.alias, tp2) - case _ => false - } - } def compareNamed = { - implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type - tp1 match { - case tp1: NamedType => - val sym1 = tp1.symbol - compareAlias(tp1.info) || - (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) - ctx.erasedTypes || sym1.isStaticOwner || isSubType(tp1.prefix, tp2.prefix) - else - (tp1.name eq tp2.name) && - isSubType(tp1.prefix, tp2.prefix) && - (tp1.signature == tp2.signature) && - !tp1.isInstanceOf[WithFixedSym] && - !tp2.isInstanceOf[WithFixedSym] - ) || - compareHK(tp1, tp2, inOrder = true) || - compareHK(tp2, tp1, inOrder = false) || - thirdTryNamed(tp1, tp2) - case _ => - compareHK(tp2, tp1, inOrder = false) || - compareAlias(NoType) || - secondTry(tp1, tp2) + implicit val ctx: Context = this.ctx + tp2.info match { + case info2: TypeAlias => firstTry(tp1, info2.alias) + case _ => tp1 match { + case tp1: NamedType => + tp1.info match { + case info1: TypeAlias => firstTry(info1.alias, tp2) + case _ => + val sym1 = tp1.symbol + (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) + ctx.erasedTypes || + sym1.isStaticOwner || + isSubType(tp1.prefix, tp2.prefix) || + thirdTryNamed(tp1, tp2) + else + (tp1.name eq tp2.name) && + isSubType(tp1.prefix, tp2.prefix) && + (tp1.signature == tp2.signature) && + !tp1.isInstanceOf[WithFixedSym] && + !tp2.isInstanceOf[WithFixedSym] || + compareHK(tp1, tp2, inOrder = true) || + compareHK(tp2, tp1, inOrder = false) || + thirdTryNamed(tp1, tp2)) + } + case _ => + compareHK(tp2, tp1, inOrder = false) || + secondTry(tp1, tp2) + } } } compareNamed From 4ca8744da021642d9f943224950482b3344cf089 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Nov 2015 18:17:54 +0100 Subject: [PATCH 2/5] Avoid too eager computation in reporting. I noted a slowdown of about 25% (66sec -> 81sec) when compiling dotty even after the subtype optimization (before it was 117sec). I tracked it down to the traceIndented fix which avoided questions to be evaluated twice. But it also caused the question to be evaluated Making the val lazy fixed the problem. --- src/dotty/tools/dotc/reporting/Reporter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala index 5cad0a0770bc..f98d85ce9d7f 100644 --- a/src/dotty/tools/dotc/reporting/Reporter.scala +++ b/src/dotty/tools/dotc/reporting/Reporter.scala @@ -155,7 +155,7 @@ trait Reporting { this: Context => else { // Avoid evaluating question multiple time, since each evaluation // may cause some extra logging output. - val q: String = question + lazy val q: String = question traceIndented[T](s"==> $q?", (res: Any) => s"<== $q = ${resStr(res)}")(op) } } From e77428eb0bf0d9f68f4055d686cbabe111a4afdf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 29 Nov 2015 19:11:18 +0100 Subject: [PATCH 3/5] Drop argumnt interpolation. It turns out it's not needed because now all type arguments are expressed as aliases. Interestingly dropping this feature shaved 20% off the time off junit tests. Which seems to indicate that the handling of type application is really performance critical. --- .../tools/dotc/core/TypeApplications.scala | 24 ++++++------------- src/dotty/tools/dotc/core/TypeComparer.scala | 6 ++--- .../tools/dotc/core/tasty/TreePickler.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 2 +- 5 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index be070dace030..a023322acba0 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -286,7 +286,7 @@ class TypeApplications(val self: Type) extends AnyVal { */ final def baseArgInfos(base: Symbol)(implicit ctx: Context): List[Type] = if (self derivesFrom base) - base.typeParams map (param => self.member(param.name).info.argInfo(param)) + base.typeParams map (param => self.member(param.name).info.argInfo) else Nil @@ -311,7 +311,7 @@ class TypeApplications(val self: Type) extends AnyVal { /** The first type argument of the base type instance wrt `base` of this type */ final def firstBaseArgInfo(base: Symbol)(implicit ctx: Context): Type = base.typeParams match { case param :: _ if self derivesFrom base => - self.member(param.name).info.argInfo(param) + self.member(param.name).info.argInfo case _ => NoType } @@ -371,7 +371,7 @@ class TypeApplications(val self: Type) extends AnyVal { * Existential types in arguments are returned as TypeBounds instances. * @param interpolate See argInfo */ - final def argInfos(interpolate: Boolean)(implicit ctx: Context): List[Type] = { + final def argInfos(implicit ctx: Context): List[Type] = { var tparams: List[TypeSymbol] = null def recur(tp: Type, refineCount: Int): mutable.ListBuffer[Type] = tp.stripTypeVar match { case tp @ RefinedType(tycon, name) => @@ -381,7 +381,7 @@ class TypeApplications(val self: Type) extends AnyVal { if (tparams == null) tparams = tycon.typeParams if (buf.size < tparams.length) { val tparam = tparams(buf.size) - if (name == tparam.name) buf += tp.refinedInfo.argInfo(tparam, interpolate) + if (name == tparam.name) buf += tp.refinedInfo.argInfo else null } else null } @@ -393,8 +393,6 @@ class TypeApplications(val self: Type) extends AnyVal { if (buf == null) Nil else buf.toList } - final def argInfos(implicit ctx: Context): List[Type] = argInfos(interpolate = true) - /** Argument types where existential types in arguments are disallowed */ def argTypes(implicit ctx: Context) = argInfos mapConserve noBounds @@ -428,18 +426,10 @@ class TypeApplications(val self: Type) extends AnyVal { * * for a contravariant type-parameter becomes L. */ - final def argInfo(tparam: Symbol, interpolate: Boolean = true)(implicit ctx: Context): Type = self match { + final def argInfo(implicit ctx: Context): Type = self match { case self: TypeAlias => self.alias - case TypeBounds(lo, hi) => - if (interpolate) { - val v = tparam.variance - if (v > 0 && (lo isRef defn.NothingClass)) hi - else if (v < 0 && (hi isRef defn.AnyClass)) lo - else self - } - else self - case _ => - NoType + case self: TypeBounds => self + case _ => NoType } /** The element type of a sequence or array */ diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index d94e244694b0..d0abe86a7f1a 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -140,14 +140,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { private def firstTry(tp1: Type, tp2: Type): Boolean = tp2 match { case tp2: NamedType => - def compareNamed = { + def compareNamed(tp1: Type, tp2: NamedType): Boolean = { implicit val ctx: Context = this.ctx tp2.info match { case info2: TypeAlias => firstTry(tp1, info2.alias) case _ => tp1 match { case tp1: NamedType => tp1.info match { - case info1: TypeAlias => firstTry(info1.alias, tp2) + case info1: TypeAlias => compareNamed(info1.alias, tp2) case _ => val sym1 = tp1.symbol (if ((sym1 ne NoSymbol) && (sym1 eq tp2.symbol)) @@ -171,7 +171,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } } } - compareNamed + compareNamed(tp1, tp2) case tp2: ProtoType => isMatchedByProto(tp2, tp1) case tp2: BoundType => diff --git a/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/src/dotty/tools/dotc/core/tasty/TreePickler.scala index d98ebb4d1c01..c0136538bb47 100644 --- a/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -211,7 +211,7 @@ class TreePickler(pickler: TastyPickler) { case tpe: SkolemType => pickleType(tpe.info) case tpe: RefinedType => - val args = tpe.argInfos(interpolate = false) + val args = tpe.argInfos if (args.isEmpty) { writeByte(REFINEDtype) withLength { diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 7065976dc4c8..06fe0c9eff17 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -109,7 +109,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } homogenize(tp) match { case tp: RefinedType => - val args = tp.argInfos(interpolate = false) + val args = tp.argInfos if (args.nonEmpty) { val tycon = tp.unrefine val cls = tycon.typeSymbol diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 999efff090ed..5eebdbad1cc8 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -848,7 +848,7 @@ class Namer { typer: Typer => def apply(tp: Type): Type = { tp match { case tp: RefinedType => - val args = tp.argInfos(interpolate = false).mapconserve(this) + val args = tp.argInfos.mapconserve(this) if (args.nonEmpty) { val tycon = tp.withoutArgs(args) val tparams = tycon.typeParams From 559801288d96a28e84f4b79c9033b507416caa90 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 30 Nov 2015 08:48:03 +0100 Subject: [PATCH 4/5] Fixed comments --- src/dotty/tools/dotc/core/TypeApplications.scala | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index a023322acba0..3d9ffb0bb6fa 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -369,7 +369,6 @@ class TypeApplications(val self: Type) extends AnyVal { /** If this is an encoding of a (partially) applied type, return its arguments, * otherwise return Nil. * Existential types in arguments are returned as TypeBounds instances. - * @param interpolate See argInfo */ final def argInfos(implicit ctx: Context): List[Type] = { var tparams: List[TypeSymbol] = null @@ -415,16 +414,6 @@ class TypeApplications(val self: Type) extends AnyVal { /** If this is the image of a type argument to type parameter `tparam`, * recover the type argument, otherwise NoType. - * @param interpolate If true, replace type bounds as arguments corresponding to - * variant type parameters by their dominating element. I.e. an argument - * - * T <: U - * - * for a covariant type-parameter becomes U, and an argument - * - * T >: L - * - * for a contravariant type-parameter becomes L. */ final def argInfo(implicit ctx: Context): Type = self match { case self: TypeAlias => self.alias From 9e7e40b9f33aec952a9fcb05d45734dd19da0322 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Mon, 30 Nov 2015 17:19:07 +0100 Subject: [PATCH 5/5] Update the comment of TypeApplications#argInfo --- src/dotty/tools/dotc/core/TypeApplications.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 3d9ffb0bb6fa..661975dabce9 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -412,8 +412,8 @@ class TypeApplications(val self: Type) extends AnyVal { self } - /** If this is the image of a type argument to type parameter `tparam`, - * recover the type argument, otherwise NoType. + /** If this is the image of a type argument; recover the type argument, + * otherwise NoType. */ final def argInfo(implicit ctx: Context): Type = self match { case self: TypeAlias => self.alias