Skip to content

Commit

Permalink
More aggressive reduction of type selection (fixes parboiled2)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
smarter committed Apr 20, 2022
1 parent 119e3d7 commit be9c8be
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 5 deletions.
9 changes: 5 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Expand Up @@ -1548,9 +1548,10 @@ object Types {
@tailrec def loop(pre: Type): Type = pre.stripTypeVar match {
case pre: RefinedType =>
pre.refinedInfo match {
case TypeAlias(alias) =>
if (pre.refinedName ne name) loop(pre.parent) else alias
case _ => loop(pre.parent)
case TypeBounds(lo, hi) if lo eq hi =>
if (pre.refinedName ne name) loop(pre.parent) else lo
case _ =>
loop(pre.parent)
}
case pre: RecType =>
val candidate = pre.parent.lookupRefined(name)
Expand Down Expand Up @@ -4640,7 +4641,7 @@ object Types {
myRepr.nn
}

override def toString: String = s"Skolem($hashCode)"
override def toString: String = s"SkolemType($hashCode)"
}

/** A skolem type used to wrap the type of the qualifier of a selection.
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/13491.scala
Expand Up @@ -87,7 +87,7 @@ object Rule {

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

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

object Test {
Expand Down
22 changes: 22 additions & 0 deletions tests/pos/i14903a.scala
@@ -0,0 +1,22 @@
trait Wrapper[T] {
type Out
}

type Func[T] =
T match {
case String => Long
case Long => Int
case Int => Float
case Float => Double
case Double => Unit
case Unit => String
}

implicit def infer[A]: Wrapper[One[A]] { type Out = Func[A] } = ???

trait One[A] {
def use(implicit w: Wrapper[One[A]]): One[w.Out]
}

val x: One[Long] = null
val _ = x.use.use.use.use.use.use.use
54 changes: 54 additions & 0 deletions tests/pos/i14903b.scala
@@ -0,0 +1,54 @@
import annotation.unchecked.uncheckedVariance

sealed trait HList
sealed trait HNil extends HList
case object HNil extends HNil
case class ::[+H, +T <: HList](head: H, tail: T) extends HList

type Concat[X <: HList, Y <: HList] <: HList = X match
case HNil => Y
case h :: t => h :: Concat[t, Y]

/**
* Decompose L into Prefix ++ Suffix if possible
*/
type StripSuffix[L <: HList, Suffix <: HList] <: Option[HList] = L match
case Suffix => Some[HNil]
case h :: t => StripSuffix[t, Suffix] match
case Some[x] => Some[h :: x]
case _ => None.type
case _ => None.type

/**
* type-level implementation of this logic:
* Out =
* R if T has a tail of type L
* (L dropRight T) ++ R if L has a tail of type T
*/
sealed trait TailSwitch[L <: HList, T <: HList, R <: HList]:
type Out <: HList

object TailSwitch:
type TS[L <: HList, T <: HList, R <: HList] <: HList =
StripSuffix[T, L] match
case Some[_] => R
case _ => StripSuffix[L, T] match
case Some[x] => Concat[x, R]

implicit def tailSwitch[L <: HList, T <: HList, R <: HList]: (TailSwitch[L, T, R] {
type Out = TS[L, T, R]
}) = new TailSwitch[L, T, R] { type Out = TS[L, T, R] }

/**
* Rule popping I from stack and pushing back O
*/
sealed class Rule[-I <: HList, +O <: HList]:
def ~[I2 <: HList, O2 <: HList](that: Rule[I2, O2])(implicit
i: TailSwitch[I2, O @uncheckedVariance, I @uncheckedVariance],
o: TailSwitch[O @uncheckedVariance, I2, O2]
): Rule[i.Out, o.Out] = ???

object Test:
def dot = new Rule[HNil, HNil] {}
def num = new Rule[HNil, Byte :: HNil] {}
def pattern = num ~ dot ~ num ~ dot ~ num ~ dot ~ num // error

0 comments on commit be9c8be

Please sign in to comment.