Skip to content

Commit be9c8be

Browse files
committed
More aggressive reduction of type selection (fixes parboiled2)
Previously, when reducing `a.T` we checked if the type of `a` was a subtype of `RefinedType(.., T, TypeAlias(...))`, now we extend this check to handle refinements where the `info` is a `TypeBounds` where both bounds are equal. This solves two big issues at once: - We can restore tests/pos/13491.scala to its original form from before #13780. The check for abstract types introduced by #13780 for soundness reasons is no longer hit because the type selection is reduced before we get to that point. This is important because parboiled2 relies on this and is therefore currently broken on 3.1.3-RC1 and main (sirthias/parboiled2#365). - This fixes #14903 (slow compilation issue affecting parboiled2) without caching skolems (as in the alternative fix #14909). Again, this is due to the type containing skolem being reducible to a simpler type and therefore cacheable.
1 parent 119e3d7 commit be9c8be

File tree

4 files changed

+82
-5
lines changed

4 files changed

+82
-5
lines changed

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

+5-4
Original file line numberDiff line numberDiff line change
@@ -1548,9 +1548,10 @@ object Types {
15481548
@tailrec def loop(pre: Type): Type = pre.stripTypeVar match {
15491549
case pre: RefinedType =>
15501550
pre.refinedInfo match {
1551-
case TypeAlias(alias) =>
1552-
if (pre.refinedName ne name) loop(pre.parent) else alias
1553-
case _ => loop(pre.parent)
1551+
case TypeBounds(lo, hi) if lo eq hi =>
1552+
if (pre.refinedName ne name) loop(pre.parent) else lo
1553+
case _ =>
1554+
loop(pre.parent)
15541555
}
15551556
case pre: RecType =>
15561557
val candidate = pre.parent.lookupRefined(name)
@@ -4640,7 +4641,7 @@ object Types {
46404641
myRepr.nn
46414642
}
46424643

4643-
override def toString: String = s"Skolem($hashCode)"
4644+
override def toString: String = s"SkolemType($hashCode)"
46444645
}
46454646

46464647
/** A skolem type used to wrap the type of the qualifier of a selection.

tests/pos/13491.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ object Rule {
8787

8888
def rule[I <: HList, O <: HList](r: Rule[I, O]): Rule[I, O] = ???
8989

90-
implicit def valueMap[T, Out0 <: HList](m: Map[String, T])(implicit h: HListable[T] { type Out = Out0 }): RuleN[Out0] = ???
90+
implicit def valueMap[T, Out0 <: HList](m: Map[String, T])(implicit h: HListable[T]): RuleN[h.Out] = ???
9191
}
9292

9393
object Test {

tests/pos/i14903a.scala

+22
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

tests/pos/i14903b.scala

+54
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

0 commit comments

Comments
 (0)