From 4d95963bdfc7ad3e630a8d5e61fcb2ce24db1353 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 2 May 2018 11:27:16 +0200 Subject: [PATCH 1/5] Fix handling of special implicit searches for ClassTag, quoted.Type, Eq They did not work if the searched-for type was an uninstantiated TypeVar that had the class as an upper bound. Getting them to work is surprisingly subtle. --- .../src/dotty/tools/dotc/core/Types.scala | 24 +++++++++++++++++++ .../dotty/tools/dotc/typer/Implicits.scala | 23 ++++++++++-------- tests/run/inlineForeach.scala | 19 +++++++++++++++ tests/run/patmatch-classtag.scala | 2 +- 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 9e08d013b1b8..570932c800cd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -863,6 +863,30 @@ object Types { } } + /** Like basetype, but follow context bounds */ + final def contextualBaseType(cls: ClassSymbol)(implicit ctx: Context): Type = { + def loop(tp: Type): Type = tp match { + case tp: TypeRef => + val sym = tp.symbol + if (sym.isClass) tp.baseType(cls) else loop(tp.superType): @tailrec + case tp: AppliedType => + tp.baseType(cls) + case tp: TypeParamRef => + loop(ctx.typeComparer.bounds(tp).hi): @tailrec + case tp: TypeProxy => + loop(tp.superType): @tailrec + case tp: AndType => + loop(tp.tp1) & loop(tp.tp2): @tailrec + case tp: OrType => + loop(tp.tp1) | loop(tp.tp2): @tailrec + case tp: JavaArrayType => + if (cls == defn.ObjectClass) cls.typeRef else NoType + case _ => + NoType + } + loop(this) + } + def & (that: Type)(implicit ctx: Context): Type = track("&") { ctx.typeComparer.glb(this, that) } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 5990b102dac0..f660d5388d21 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -650,17 +650,20 @@ trait Implicits { self: Typer => ref(lazyImplicit)) else arg - case fail @ SearchFailure(tree) => - if (fail.isAmbiguous) - tree - else if (formalValue.isRef(defn.ClassTagClass)) - synthesizedClassTag(formalValue).orElse(tree) - else if (formalValue.isRef(defn.QuotedTypeClass)) - synthesizedTypeTag(formalValue).orElse(tree) - else if (formalValue.isRef(defn.EqClass)) - synthesizedEq(formalValue).orElse(tree) + case fail @ SearchFailure(failed) => + def trySpecialCase(cls: ClassSymbol, handler: Type => Tree, ifNot: => Tree) = { + val base = formalValue.contextualBaseType(cls) + if (base <:< formalValue) { + // With the subtype test we enforce that the searched type `formalValue` is of the right form + handler(base).orElse(ifNot) + } + else ifNot + } + if (fail.isAmbiguous) failed else - tree + trySpecialCase(defn.ClassTagClass, synthesizedClassTag, + trySpecialCase(defn.QuotedTypeClass, synthesizedTypeTag, + trySpecialCase(defn.EqClass, synthesizedEq, failed))) } } diff --git a/tests/run/inlineForeach.scala b/tests/run/inlineForeach.scala index 1389ad6c4059..437e58cbfc5f 100644 --- a/tests/run/inlineForeach.scala +++ b/tests/run/inlineForeach.scala @@ -35,6 +35,22 @@ object Test { zs } + implicit class intArrayOps(arr: Array[Int]) { + inline def foreach(inline op: Int => Unit): Unit = { + var i = 0 + while (i < arr.length) { + op(arr(i)) + i += 1 + } + } + } + + def sum(ints: Array[Int]): Int = { + var t = 0 + for (n <- ints) t += n + t + } + def main(args: Array[String]) = { 1.until(10).foreach(i => println(i)) 1.until(10).foreach(println(_)) @@ -44,5 +60,8 @@ object Test { for (k1 <- 1 to 10) for (k2 <- 1 to 10) println(s"$k1") + + val xs = Array(1, 2, 3, 4) + assert(sum(xs) == 10, sum(xs)) } } diff --git a/tests/run/patmatch-classtag.scala b/tests/run/patmatch-classtag.scala index f1f6645175e0..99e1ee3f05ae 100644 --- a/tests/run/patmatch-classtag.scala +++ b/tests/run/patmatch-classtag.scala @@ -18,7 +18,7 @@ object dotc { object Impl extends API { type CaseDef = dotc.CaseDef - val tagForCaseDef: ClassTag[dotc.CaseDef] = implicitly[ClassTag[dotc.CaseDef]] + val tagForCaseDef: ClassTag[dotc.CaseDef] = implicitly object CaseDef extends CaseDefCompanion { def apply(str: String): CaseDef = dotc.CaseDef(str) From 25c720f4f1bfe8ab3effaa53cc30084bdc2bdf47 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 2 May 2018 12:33:58 +0200 Subject: [PATCH 2/5] Mad baseType more context dependent Roll the functionality of contextualBaseType into baseType itself. --- .../tools/dotc/core/SymDenotations.scala | 26 ++++++++++++------- .../src/dotty/tools/dotc/core/Types.scala | 24 ----------------- .../dotty/tools/dotc/typer/Implicits.scala | 2 +- 3 files changed, 18 insertions(+), 34 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index af680510de14..d74dfddd4e7a 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1636,15 +1636,16 @@ object SymDenotations { * cache ErasedValueType at all. */ def isCachable(tp: Type, btrCache: BaseTypeMap): Boolean = { - def inCache(tp: Type) = btrCache.containsKey(tp) + def inCache(tp: Type) = btrCache.containsKey(tp) && isCachable(tp, btrCache) tp match { - case _: TypeErasure.ErasedValueType => false case tp: TypeRef if tp.symbol.isClass => true case tp: TypeVar => tp.inst.exists && inCache(tp.inst) + case tp: TypeParamRef if ctx.typerState.constraint.contains(tp) => false //case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing case tp: TypeProxy => isCachable(tp.underlying, btrCache) case tp: AndType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) case tp: OrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) + case _: TypeErasure.ErasedValueType => false case _ => true } } @@ -1685,6 +1686,8 @@ object SymDenotations { case tparams: List[Symbol @unchecked] => baseTypeOf(tycon).subst(tparams, args) } + case tp: TypeParamRef => + baseTypeOf(ctx.typeComparer.bounds(tp).hi) case tp: TypeProxy => baseTypeOf(tp.superType) case AndType(tp1, tp2) => @@ -1709,19 +1712,24 @@ object SymDenotations { tp.stripTypeVar match { case tp: CachedType => val btrCache = baseTypeCache + if (!isCachable(tp, btrCache)) + computeBaseTypeOf(tp) + else try { - var basetp = btrCache get tp + var basetp = btrCache.get(tp) if (basetp == null) { btrCache.put(tp, NoPrefix) basetp = computeBaseTypeOf(tp) - if (!basetp.exists) Stats.record("base type miss") - if (isCachable(tp, btrCache)) { - if (basetp.exists) Stats.record("cached base type hit") - else Stats.record("cached base type miss") + if (basetp.exists) { + Stats.record("cached base type exists") btrCache.put(tp, basetp) } - else btrCache.remove(tp) - } else if (basetp `eq` NoPrefix) + else { + Stats.record("cached base type missing") + btrCache.remove(tp) + } + } + else if (basetp `eq` NoPrefix) throw CyclicReference(this) basetp } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 570932c800cd..9e08d013b1b8 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -863,30 +863,6 @@ object Types { } } - /** Like basetype, but follow context bounds */ - final def contextualBaseType(cls: ClassSymbol)(implicit ctx: Context): Type = { - def loop(tp: Type): Type = tp match { - case tp: TypeRef => - val sym = tp.symbol - if (sym.isClass) tp.baseType(cls) else loop(tp.superType): @tailrec - case tp: AppliedType => - tp.baseType(cls) - case tp: TypeParamRef => - loop(ctx.typeComparer.bounds(tp).hi): @tailrec - case tp: TypeProxy => - loop(tp.superType): @tailrec - case tp: AndType => - loop(tp.tp1) & loop(tp.tp2): @tailrec - case tp: OrType => - loop(tp.tp1) | loop(tp.tp2): @tailrec - case tp: JavaArrayType => - if (cls == defn.ObjectClass) cls.typeRef else NoType - case _ => - NoType - } - loop(this) - } - def & (that: Type)(implicit ctx: Context): Type = track("&") { ctx.typeComparer.glb(this, that) } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f660d5388d21..6f39e041322d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -652,7 +652,7 @@ trait Implicits { self: Typer => arg case fail @ SearchFailure(failed) => def trySpecialCase(cls: ClassSymbol, handler: Type => Tree, ifNot: => Tree) = { - val base = formalValue.contextualBaseType(cls) + val base = formalValue.baseType(cls) if (base <:< formalValue) { // With the subtype test we enforce that the searched type `formalValue` is of the right form handler(base).orElse(ifNot) From e2608cf8d59e5066b12d2a8c75067e8d33bbc573 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 2 May 2018 18:01:41 +0200 Subject: [PATCH 3/5] Fuse isCachable and computeBaseType The previous logic tended to test things several times, which made it harder to understand and possibly less efficient. We now combine the cachability decisions in the main `recur` method of `baseTypeOf`. --- .../tools/dotc/core/SymDenotations.scala | 204 ++++++++++-------- .../src/dotty/tools/dotc/core/Types.scala | 12 +- 2 files changed, 119 insertions(+), 97 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index d74dfddd4e7a..b7e083a58254 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1621,126 +1621,140 @@ object SymDenotations { /** Compute tp.baseType(this) */ final def baseTypeOf(tp: Type)(implicit ctx: Context): Type = { + val btrCache = baseTypeCache + def inCache(tp: Type) = btrCache.get(tp) != null - def foldGlb(bt: Type, ps: List[Type]): Type = ps match { - case p :: ps1 => foldGlb(bt & baseTypeOf(p), ps1) - case _ => bt + def ensureAcyclic(baseTp: Type) = { + if (baseTp `eq` NoPrefix) throw CyclicReference(this) + baseTp } - /** We cannot cache: - * - type variables which are uninstantiated or whose instances can - * change, depending on typerstate. - * - types where the underlying type is an ErasedValueType, because - * this underlying type will change after ElimErasedValueType, - * and this changes subtyping relations. As a shortcut, we do not - * cache ErasedValueType at all. - */ - def isCachable(tp: Type, btrCache: BaseTypeMap): Boolean = { - def inCache(tp: Type) = btrCache.containsKey(tp) && isCachable(tp, btrCache) + def recur(tp: Type): Type = try { tp match { - case tp: TypeRef if tp.symbol.isClass => true - case tp: TypeVar => tp.inst.exists && inCache(tp.inst) - case tp: TypeParamRef if ctx.typerState.constraint.contains(tp) => false - //case tp: TypeProxy => inCache(tp.underlying) // disabled, can re-enable insyead of last two lines for performance testing - case tp: TypeProxy => isCachable(tp.underlying, btrCache) - case tp: AndType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) - case tp: OrType => isCachable(tp.tp1, btrCache) && isCachable(tp.tp2, btrCache) - case _: TypeErasure.ErasedValueType => false - case _ => true + case tp: CachedType => + val baseTp = btrCache.get(tp) + if (baseTp != null) return ensureAcyclic(baseTp) + case _ => } - } - - def computeBaseTypeOf(tp: Type): Type = { if (Stats.monitored) { Stats.record("computeBaseType, total") Stats.record(s"computeBaseType, ${tp.getClass}") } - if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty) - symbol.typeRef - else tp match { + tp match { case tp @ TypeRef(prefix, _) => - val subsym = tp.symbol - if (subsym eq symbol) tp - else subsym.denot match { - case clsd: ClassDenotation => - val owner = clsd.owner - val isOwnThis = prefix match { - case prefix: ThisType => prefix.cls eq owner - case NoPrefix => true - case _ => false - } - if (isOwnThis) - if (clsd.baseClassSet.contains(symbol)) foldGlb(NoType, clsd.classParents) - else NoType - else - baseTypeOf(clsd.typeRef).asSeenFrom(prefix, owner) - case _ => - baseTypeOf(tp.superType) + + def foldGlb(bt: Type, ps: List[Type]): Type = ps match { + case p :: ps1 => foldGlb(bt & recur(p), ps1) + case _ => bt } + + def computeTypeRef = { + btrCache.put(tp, NoPrefix) + tp.symbol.denot match { + case clsd: ClassDenotation => + def isOwnThis = prefix match { + case prefix: ThisType => prefix.cls `eq` clsd.owner + case NoPrefix => true + case _ => false + } + val baseTp = + if (tp.symbol eq symbol) + tp + else if (isOwnThis) + if (clsd.baseClassSet.contains(symbol)) + if (symbol.isStatic && symbol.typeParams.isEmpty) symbol.typeRef + else foldGlb(NoType, clsd.classParents) + else NoType + else + recur(clsd.typeRef).asSeenFrom(prefix, clsd.owner) + if (baseTp.exists) btrCache.put(tp, baseTp) else btrCache.remove(tp) + baseTp + case _ => + val superTp = tp.superType + val baseTp = recur(superTp) + if (baseTp.exists && inCache(superTp) && tp.symbol.maybeOwner.isType) + btrCache.put(tp, baseTp) // typeref cannot be a GADT, so cache is stable + else + btrCache.remove(tp) + baseTp + } + } + computeTypeRef + case tp @ AppliedType(tycon, args) => - val subsym = tycon.typeSymbol - if (subsym eq symbol) tp - else (tycon.typeParams: @unchecked) match { - case LambdaParam(_, _) :: _ => - baseTypeOf(tp.superType) - case tparams: List[Symbol @unchecked] => - baseTypeOf(tycon).subst(tparams, args) + + def computeApplied = { + btrCache.put(tp, NoPrefix) + val baseTp = + if (tycon.typeSymbol eq symbol) tp + else (tycon.typeParams: @unchecked) match { + case LambdaParam(_, _) :: _ => + recur(tp.superType) + case tparams: List[Symbol @unchecked] => + recur(tycon).subst(tparams, args) + } + if (baseTp.exists) btrCache.put(tp, baseTp) else btrCache.remove(tp) + baseTp } - case tp: TypeParamRef => - baseTypeOf(ctx.typeComparer.bounds(tp).hi) + computeApplied + + case tp: TypeParamRef => // uncachable, since baseType depends on context bounds + recur(ctx.typeComparer.bounds(tp).hi) case tp: TypeProxy => - baseTypeOf(tp.superType) - case AndType(tp1, tp2) => - baseTypeOf(tp1) & baseTypeOf(tp2) match { - case AndType(tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp - case res => res + + def computeTypeProxy = { + val superTp = tp.superType + val baseTp = recur(superTp) + tp match { + case tp: CachedType if baseTp.exists && inCache(superTp) => + // Note: This also works for TypeVars: If they are not instantiated, their supertype + // is a TypeParamRef, which is never cached. So uninstantiated TypeVars are not cached either. + btrCache.put(tp, baseTp) + case _ => + } + baseTp } - case OrType(tp1, tp2) => - baseTypeOf(tp1) | baseTypeOf(tp2) match { - case OrType(tp1a, tp2a) if (tp1a eq tp1) && (tp2a eq tp2) => tp - case res => res + computeTypeProxy + + case tp: AndOrType => + + def computeAndOrType = { + val tp1 = tp.tp1 + val tp2 = tp.tp2 + val baseTp = + if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty) + symbol.typeRef + else { + val baseTp1 = recur(tp1) + val baseTp2 = recur(tp2) + val combined = if (tp.isAnd) baseTp1 & baseTp2 else baseTp1 | baseTp2 + combined match { + case combined: AndOrType + if (combined.tp1 eq tp1) && (combined.tp2 eq tp2) && (combined.isAnd == tp.isAnd) => tp + case _ => combined + } + } + if (baseTp.exists && inCache(tp1) && inCache(tp2)) btrCache.put(tp, baseTp) + baseTp } + computeAndOrType + case JavaArrayType(_) if symbol == defn.ObjectClass => this.typeRef case _ => NoType } } + catch { + case ex: Throwable => + btrCache.remove(tp) + throw ex + } + /*>|>*/ trace.onDebug(s"$tp.baseType($this)") /*<|<*/ { Stats.record("baseTypeOf") - tp.stripTypeVar match { - case tp: CachedType => - val btrCache = baseTypeCache - if (!isCachable(tp, btrCache)) - computeBaseTypeOf(tp) - else - try { - var basetp = btrCache.get(tp) - if (basetp == null) { - btrCache.put(tp, NoPrefix) - basetp = computeBaseTypeOf(tp) - if (basetp.exists) { - Stats.record("cached base type exists") - btrCache.put(tp, basetp) - } - else { - Stats.record("cached base type missing") - btrCache.remove(tp) - } - } - else if (basetp `eq` NoPrefix) - throw CyclicReference(this) - basetp - } - catch { - case ex: Throwable => - btrCache.put(tp, null) - throw ex - } - case tp => - computeBaseTypeOf(tp) - } + recur(tp) } } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 9e08d013b1b8..5abf8fe05880 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2438,7 +2438,14 @@ object Types { // --- AndType/OrType --------------------------------------------------------------- - abstract case class AndType(tp1: Type, tp2: Type) extends CachedGroundType with ValueType { + abstract class AndOrType extends CachedGroundType with ValueType { + def isAnd: Boolean + def tp1: Type + def tp2: Type + } + + abstract case class AndType(tp1: Type, tp2: Type) extends AndOrType { + def isAnd = true private[this] var myBaseClassesPeriod: Period = Nowhere private[this] var myBaseClasses: List[ClassSymbol] = _ /** Base classes of are the merge of the operand base classes. */ @@ -2502,7 +2509,8 @@ object Types { if (checkValid) apply(tp1, tp2) else unchecked(tp1, tp2) } - abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with ValueType { + abstract case class OrType(tp1: Type, tp2: Type) extends AndOrType { + def isAnd = false private[this] var myBaseClassesPeriod: Period = Nowhere private[this] var myBaseClasses: List[ClassSymbol] = _ /** Base classes of are the intersection of the operand base classes. */ From 3e8389a1e498a04bf0e1b662fdffaab5cbbcce64 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 2 May 2018 18:45:08 +0200 Subject: [PATCH 4/5] Better stats about recorded basetype cache entries --- .../dotty/tools/dotc/core/SymDenotations.scala | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index b7e083a58254..5247b26087c3 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1623,6 +1623,13 @@ object SymDenotations { final def baseTypeOf(tp: Type)(implicit ctx: Context): Type = { val btrCache = baseTypeCache def inCache(tp: Type) = btrCache.get(tp) != null + def record(tp: CachedType, baseTp: Type) = { + if (Stats.monitored) { + Stats.record("basetype cache entries") + if (!baseTp.exists) Stats.record("basetype cache NoTypes") + } + btrCache.put(tp, baseTp) + } def ensureAcyclic(baseTp: Type) = { if (baseTp `eq` NoPrefix) throw CyclicReference(this) @@ -1667,13 +1674,13 @@ object SymDenotations { else NoType else recur(clsd.typeRef).asSeenFrom(prefix, clsd.owner) - if (baseTp.exists) btrCache.put(tp, baseTp) else btrCache.remove(tp) + if (baseTp.exists) record(tp, baseTp) else btrCache.remove(tp) baseTp case _ => val superTp = tp.superType val baseTp = recur(superTp) if (baseTp.exists && inCache(superTp) && tp.symbol.maybeOwner.isType) - btrCache.put(tp, baseTp) // typeref cannot be a GADT, so cache is stable + record(tp, baseTp) // typeref cannot be a GADT, so cache is stable else btrCache.remove(tp) baseTp @@ -1693,7 +1700,7 @@ object SymDenotations { case tparams: List[Symbol @unchecked] => recur(tycon).subst(tparams, args) } - if (baseTp.exists) btrCache.put(tp, baseTp) else btrCache.remove(tp) + if (baseTp.exists) record(tp, baseTp) else btrCache.remove(tp) baseTp } computeApplied @@ -1709,7 +1716,7 @@ object SymDenotations { case tp: CachedType if baseTp.exists && inCache(superTp) => // Note: This also works for TypeVars: If they are not instantiated, their supertype // is a TypeParamRef, which is never cached. So uninstantiated TypeVars are not cached either. - btrCache.put(tp, baseTp) + record(tp, baseTp) case _ => } baseTp @@ -1734,7 +1741,7 @@ object SymDenotations { case _ => combined } } - if (baseTp.exists && inCache(tp1) && inCache(tp2)) btrCache.put(tp, baseTp) + if (baseTp.exists && inCache(tp1) && inCache(tp2)) record(tp, baseTp) baseTp } computeAndOrType From 0e9f17e22ba756f1414e16d03bb68f2602181bbb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 2 May 2018 18:51:55 +0200 Subject: [PATCH 5/5] Also cache unsuccessful baseType computations When testing on typer/*.scala, the differences are as follows: Cache all Only cache successful Total basetypes 386K 386K Full computations in recur 85K 397K Cache entries created 72K 46K --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 5247b26087c3..f9ead676a504 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1674,12 +1674,12 @@ object SymDenotations { else NoType else recur(clsd.typeRef).asSeenFrom(prefix, clsd.owner) - if (baseTp.exists) record(tp, baseTp) else btrCache.remove(tp) + record(tp, baseTp) baseTp case _ => val superTp = tp.superType val baseTp = recur(superTp) - if (baseTp.exists && inCache(superTp) && tp.symbol.maybeOwner.isType) + if (inCache(superTp) && tp.symbol.maybeOwner.isType) record(tp, baseTp) // typeref cannot be a GADT, so cache is stable else btrCache.remove(tp) @@ -1700,7 +1700,7 @@ object SymDenotations { case tparams: List[Symbol @unchecked] => recur(tycon).subst(tparams, args) } - if (baseTp.exists) record(tp, baseTp) else btrCache.remove(tp) + record(tp, baseTp) baseTp } computeApplied