Skip to content

Commit

Permalink
Enumerate refinement type's subtypes by deSkolemizing
Browse files Browse the repository at this point in the history
  • Loading branch information
dwijnand committed Feb 4, 2021
1 parent 3fa823b commit d14f642
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 6 deletions.
29 changes: 23 additions & 6 deletions src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala
Expand Up @@ -96,14 +96,31 @@ trait TreeAndTypeAnalysis extends Debugging {
if (parentSubtypes.exists(_.nonEmpty)) {
// If any of the parents is enumerable, then the refinement type is enumerable.
// We must only include subtypes of the parents that conform to `tpApprox`.
// See neg/virtpatmat_exhaust_compound.scala and pos/t9657.scala for examples.
val approximateTypeSkolemsToUpperBound = new TypeMap { // from approximateAbstracts
def apply(tp: Type): Type = tp.dealiasWiden match {
case TypeRef(_, sym, _) if sym.isTypeSkolem => tp.upperBound
case _ => mapOver(tp)
// See neg/virtpatmat_exhaust_compound.scala for a base example,
// and pos/t9657.scala & pos/t9657-less-fishy.scala for examples involving type skolems.
val deskolemizeAliases = new TypeMap { // from approximateAbstracts
def apply(tp: Type) = mapOver(tp)

// The default only allows mapping the types of the symbols
// but we need to map the symbols themselves
override def mapOver(syms: List[Symbol]) = syms.mapConserve(deskolemizeAlias)

/** Replace any alias type symbols which dealias to type skolems
* with new abstract types, assigning the de-skolemized's type to it.
*
* For example, in `def m[A <: B](x: X { type U = A }): X = x`
* replace `type U = A` with `type U <: B`.
*/
private def deskolemizeAlias(sym: Symbol) = {
val dealiasedSym = sym.info.typeSymbol
if (dealiasedSym.isTypeSkolem) {
sym.owner.newAbstractType(sym.name.toTypeName, sym.pos, sym.flags).setInfo {
dealiasedSym.deSkolemize.info
}
} else sym
}
}
val tpApprox = approximateTypeSkolemsToUpperBound(tp)
val tpApprox = deskolemizeAliases(tp)
parentSubtypes.map(_.filter(_ <:< tpApprox))
} else Nil
}
Expand Down
28 changes: 28 additions & 0 deletions test/files/pos/t9657-less-fishy.scala
@@ -0,0 +1,28 @@
// scalac: -Werror

// like pos/t9657
// but with a more plausible reason for why to have a method type parameter

sealed trait PowerSource
sealed trait NonRenewablePowerSource extends PowerSource

case object Petrol extends NonRenewablePowerSource
case object Coal extends NonRenewablePowerSource
case object Pedal extends PowerSource

sealed abstract class Vehicle { type A <: PowerSource }
case object Bicycle extends Vehicle { type A = Pedal.type }
case class Bus(fuel: Int) extends Vehicle { type A = Petrol.type }
case class Car(fuel: Int) extends Vehicle { type A = Petrol.type }

object Test {
def refuel[P <: NonRenewablePowerSource](vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning
}

def main(args: Array[String]): Unit = {
println(refuel(Car(100)))
println(refuel(Bus(5)))
}
}
9 changes: 9 additions & 0 deletions test/pending/neg/t9657-less-fishy.check
@@ -0,0 +1,9 @@
t9657-less-fishy.scala:27: warning: unreachable code
case Bicycle => ??? // expected unreachable 1
^
t9657-less-fishy.scala:33: warning: unreachable code
case _: Bicycle.type => ??? // expected unreachable 2
^
error: No warnings can be incurred under -Werror.
2 warnings
1 error
49 changes: 49 additions & 0 deletions test/pending/neg/t9657-less-fishy.scala
@@ -0,0 +1,49 @@
// scalac: -Werror

// like {pos,neg}/t9657
// but with a more plausible reason for why to have a method type parameter

sealed trait PowerSource
sealed trait NonRenewablePowerSource extends PowerSource

case object Petrol extends NonRenewablePowerSource
case object Coal extends NonRenewablePowerSource
case object Pedal extends PowerSource

sealed abstract class Vehicle { type A <: PowerSource }
case object Bicycle extends Vehicle { type A = Pedal.type }
case class Bus(fuel: Int) extends Vehicle { type A = Petrol.type }
case class Car(fuel: Int) extends Vehicle { type A = Petrol.type }

object Test {
def refuel2[P <: NonRenewablePowerSource](vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 2
case Bicycle => ??? // expected unreachable 1
}

def refuel3[P <: NonRenewablePowerSource](vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 3
case _: Bicycle.type => ??? // expected unreachable 2
}
}

class Test[P <: NonRenewablePowerSource] {
def refuel(vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 4
}

def refuel2(vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 5
case Bicycle => ??? // expected unreachable 3
}

def refuel3(vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 6
case _: Bicycle.type => ??? // expected unreachable 4
}
}
9 changes: 9 additions & 0 deletions test/pending/neg/t9657.check
@@ -0,0 +1,9 @@
t9657.scala:15: warning: unreachable code
case Bicycle => ??? // expected unreachable 1
^
t9657.scala:21: warning: unreachable code
case _: Bicycle.type => ??? // expected unreachable 2
^
error: No warnings can be incurred under -Werror.
2 warnings
1 error
37 changes: 37 additions & 0 deletions test/pending/neg/t9657.scala
@@ -0,0 +1,37 @@
// scalac: -Werror
sealed trait PowerSource
case object Petrol extends PowerSource
case object Pedal extends PowerSource

sealed abstract class Vehicle { type A <: PowerSource }
case object Bicycle extends Vehicle { type A = Pedal.type }
case class Bus(fuel: Int) extends Vehicle { type A = Petrol.type }
case class Car(fuel: Int) extends Vehicle { type A = Petrol.type }

object Test {
def refuel1[P <: Petrol.type](vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 1
case Bicycle => ??? // expected unreachable 1
}

def refuel2[P <: Petrol.type](vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 2
case _: Bicycle.type => ??? // expected unreachable 2
}
}

class Test[P <: Petrol.type] {
def refuel1(vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 3
case Bicycle => ??? // expected unreachable 3
}

def refuel2(vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 4
case _: Bicycle.type => ??? // expected unreachable 4
}
}
23 changes: 23 additions & 0 deletions test/pending/pos/t9657-less-fishy.scala
@@ -0,0 +1,23 @@
// scalac: -Werror

// like pos/t9657
// but with a more plausible reason for why to have a method type parameter

sealed trait PowerSource
sealed trait NonRenewablePowerSource extends PowerSource

case object Petrol extends NonRenewablePowerSource
case object Coal extends NonRenewablePowerSource
case object Pedal extends PowerSource

sealed abstract class Vehicle { type A <: PowerSource }
case object Bicycle extends Vehicle { type A = Pedal.type }
case class Bus(fuel: Int) extends Vehicle { type A = Petrol.type }
case class Car(fuel: Int) extends Vehicle { type A = Petrol.type }

class Test[P <: NonRenewablePowerSource] {
def refuel(vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 2
}
}
16 changes: 16 additions & 0 deletions test/pending/pos/t9657.scala
@@ -0,0 +1,16 @@
// scalac: -Werror
sealed trait PowerSource
case object Petrol extends PowerSource
case object Pedal extends PowerSource

sealed abstract class Vehicle { type A <: PowerSource }
case object Bicycle extends Vehicle { type A = Pedal.type }
case class Bus(fuel: Int) extends Vehicle { type A = Petrol.type }
case class Car(fuel: Int) extends Vehicle { type A = Petrol.type }

class Test[P <: Petrol.type] {
def refuel(vehicle: Vehicle { type A = P }): Vehicle = vehicle match {
case Car(_) => Car(100)
case Bus(_) => Bus(100) // was: "unreachable code" warning 2
}
}

0 comments on commit d14f642

Please sign in to comment.