Skip to content

Commit 72b855f

Browse files
committed
Merge pull request scala#4828 from retronym/topic/existential-contains
Attacking exponential complexity in TypeMaps
2 parents c7040b6 + 238b1fb commit 72b855f

File tree

5 files changed

+44
-13
lines changed

5 files changed

+44
-13
lines changed

src/reflect/scala/reflect/internal/Variances.scala

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,21 @@ trait Variances {
122122
* same is true of the parameters (ValDefs) unless we are inside a
123123
* refinement, in which case they are checked from here.
124124
*/
125-
def apply(tp: Type): Type = tp match {
126-
case _ if isUncheckedVariance(tp) => tp
127-
case _ if resultTypeOnly(tp) => this(tp.resultType)
128-
case TypeRef(_, sym, _) if sym.isAliasType => this(tp.normalize)
129-
case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; mapOver(tp)
130-
case RefinedType(_, _) => withinRefinement(mapOver(tp))
131-
case ClassInfoType(parents, _, _) => parents foreach this ; tp
132-
case mt @ MethodType(_, result) => flipped(mt.paramTypes foreach this) ; this(result)
133-
case _ => mapOver(tp)
125+
def apply(tp: Type): Type = {
126+
tp match {
127+
case _ if isUncheckedVariance(tp) =>
128+
case _ if resultTypeOnly(tp) => this(tp.resultType)
129+
case TypeRef(_, sym, _) if sym.isAliasType => this(tp.normalize)
130+
case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; mapOver(tp)
131+
case RefinedType(_, _) => withinRefinement(mapOver(tp))
132+
case ClassInfoType(parents, _, _) => parents foreach this
133+
case mt @ MethodType(_, result) => flipped(mt.paramTypes foreach this) ; this(result)
134+
case _ => mapOver(tp)
135+
}
136+
// We're using TypeMap here for type traversal only. To avoid wasteful symbol
137+
// cloning during the recursion, it is important to return the input `tp`, rather
138+
// than the result of the pattern match above, which normalizes types.
139+
tp
134140
}
135141
def validateDefinition(base: Symbol) {
136142
val saved = this.base

src/reflect/scala/reflect/internal/tpe/TypeMaps.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -998,10 +998,20 @@ private[internal] trait TypeMaps {
998998
class ContainsCollector(sym: Symbol) extends TypeCollector(false) {
999999
def traverse(tp: Type) {
10001000
if (!result) {
1001-
tp.normalize match {
1002-
case TypeRef(_, sym1, _) if (sym == sym1) => result = true
1003-
case SingleType(_, sym1) if (sym == sym1) => result = true
1004-
case _ => mapOver(tp)
1001+
tp match {
1002+
case _: ExistentialType =>
1003+
// ExistentialType#normalize internally calls contains, which leads to exponential performance
1004+
// for types like: `A[_ <: B[_ <: ... ]]`. Example: pos/existential-contains.scala.
1005+
//
1006+
// We can just map over the components and wait until we see the underlying type before we call
1007+
// normalize.
1008+
mapOver(tp)
1009+
case _ =>
1010+
tp.normalize match {
1011+
case TypeRef(_, sym1, _) if (sym == sym1) => result = true
1012+
case SingleType(_, sym1) if (sym == sym1) => result = true
1013+
case _ => mapOver(tp)
1014+
}
10051015
}
10061016
}
10071017
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class C {
2+
class L[+A]
3+
def test = {
4+
val foo:
5+
L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: _ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: _ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_]]]]]]]]]]]]]]]]]]]]]]]]
6+
= ??? } }
7+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-Ystop-after:refchecks
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class C {
2+
type L[+A] = scala.collection.immutable.List[A]
3+
def test = {
4+
val foo:
5+
L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: _ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: _ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_ <: L[_]]]]]]]]]]]]]]]]]]]]]]]]
6+
= ??? } }
7+

0 commit comments

Comments
 (0)