Skip to content

Commit 3a2c947

Browse files
committed
Cache skolem types
Skolem types were not cached, which means that any type containing a skolem type was not cached either. This meant that the same match type with a skolem type as selector was created many times instead of once, so its reduction was not cached either. We now cache skolem types. It's a bet that in practice few skolem types are created and that therefore the hashtable pollution with skolemtypes is less of a problem than the potential problem of losing identity of types containing skolem types. Fixes #14903
1 parent d673b97 commit 3a2c947

File tree

3 files changed

+81
-3
lines changed

3 files changed

+81
-3
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4618,12 +4618,14 @@ object Types {
46184618
* Note that care is needed when creating them, since not all types need to be inhabited.
46194619
* A skolem is equal to itself and no other type.
46204620
*/
4621-
case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType {
4621+
case class SkolemType(info: Type) extends CachedProxyType with ValueType with SingletonType {
46224622
override def underlying(using Context): Type = info
46234623
def derivedSkolemType(info: Type)(using Context): SkolemType =
46244624
if (info eq this.info) this else SkolemType(info)
4625-
override def hashCode: Int = System.identityHashCode(this)
4626-
override def equals(that: Any): Boolean = this.eq(that.asInstanceOf[AnyRef])
4625+
4626+
override def computeHash(bs: Binders): Int = System.identityHashCode(this)
4627+
override def eql(that: Type): Boolean = this eq that
4628+
46274629

46284630
def withName(name: Name): this.type = { myRepr = name; this }
46294631

tests/neg/i14903.scala

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import annotation.unchecked.uncheckedVariance
2+
3+
sealed trait HList
4+
sealed trait HNil extends HList
5+
case object HNil extends HNil
6+
case class ::[+H, +T <: HList](head: H, tail: T) extends HList
7+
8+
type Concat[X <: HList, Y <: HList] <: HList = X match
9+
case HNil => Y
10+
case h :: t => h :: Concat[t, Y]
11+
12+
/**
13+
* Decompose L into Prefix ++ Suffix if possible
14+
*/
15+
type StripSuffix[L <: HList, Suffix <: HList] <: Option[HList] = L match
16+
case Suffix => Some[HNil]
17+
case h :: t => StripSuffix[t, Suffix] match
18+
case Some[x] => Some[h :: x]
19+
case _ => None.type
20+
case _ => None.type
21+
22+
/**
23+
* type-level implementation of this logic:
24+
* Out =
25+
* R if T has a tail of type L
26+
* (L dropRight T) ++ R if L has a tail of type T
27+
*/
28+
sealed trait TailSwitch[L <: HList, T <: HList, R <: HList]:
29+
type Out <: HList
30+
31+
object TailSwitch:
32+
type TS[L <: HList, T <: HList, R <: HList] <: HList =
33+
StripSuffix[T, L] match
34+
case Some[_] => R
35+
case _ => StripSuffix[L, T] match
36+
case Some[x] => Concat[x, R]
37+
38+
implicit def tailSwitch[L <: HList, T <: HList, R <: HList]: (TailSwitch[L, T, R] {
39+
type Out = TS[L, T, R]
40+
}) = new TailSwitch[L, T, R] { type Out = TS[L, T, R] }
41+
42+
/**
43+
* Rule popping I from stack and pushing back O
44+
*/
45+
sealed class Rule[-I <: HList, +O <: HList]:
46+
def ~[I2 <: HList, O2 <: HList](that: Rule[I2, O2])(implicit
47+
i: TailSwitch[I2, O @uncheckedVariance, I @uncheckedVariance],
48+
o: TailSwitch[O @uncheckedVariance, I2, O2]
49+
): Rule[i.Out, o.Out] = ???
50+
51+
object Test:
52+
def dot = new Rule[HNil, HNil] {}
53+
def num = new Rule[HNil, Byte :: HNil] {}
54+
def pattern = num ~ dot ~ num ~ dot ~ num ~ dot ~ num // error

tests/pos/i14903.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
trait Wrapper[T] {
2+
type Out
3+
}
4+
5+
type Func[T] =
6+
T match {
7+
case String => Long
8+
case Long => Int
9+
case Int => Float
10+
case Float => Double
11+
case Double => Unit
12+
case Unit => String
13+
}
14+
15+
implicit def infer[A]: Wrapper[One[A]] { type Out = Func[A] } = ???
16+
17+
trait One[A] {
18+
def use(implicit w: Wrapper[One[A]]): One[w.Out]
19+
}
20+
21+
val x: One[Long] = null
22+
val _ = x.use.use.use.use.use.use.use

0 commit comments

Comments
 (0)