diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 281058cdcad0..4c8c6889770b 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -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 } diff --git a/test/files/pos/t9657-less-fishy.scala b/test/files/pos/t9657-less-fishy.scala new file mode 100644 index 000000000000..e8d752a2e9aa --- /dev/null +++ b/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))) + } +} diff --git a/test/pending/neg/t9657-less-fishy.check b/test/pending/neg/t9657-less-fishy.check new file mode 100644 index 000000000000..d9df208c93c5 --- /dev/null +++ b/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 diff --git a/test/pending/neg/t9657-less-fishy.scala b/test/pending/neg/t9657-less-fishy.scala new file mode 100644 index 000000000000..d1f3518beea4 --- /dev/null +++ b/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 + } +} diff --git a/test/pending/neg/t9657.check b/test/pending/neg/t9657.check new file mode 100644 index 000000000000..c49c763e7610 --- /dev/null +++ b/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 diff --git a/test/pending/neg/t9657.scala b/test/pending/neg/t9657.scala new file mode 100644 index 000000000000..aea6909eccd7 --- /dev/null +++ b/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 + } +} diff --git a/test/pending/pos/t9657-less-fishy.scala b/test/pending/pos/t9657-less-fishy.scala new file mode 100644 index 000000000000..a4d7a09750bc --- /dev/null +++ b/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 + } +} diff --git a/test/pending/pos/t9657.scala b/test/pending/pos/t9657.scala new file mode 100644 index 000000000000..09522e23e012 --- /dev/null +++ b/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 + } +}