From e1e9f25f24397f4123123afc3c83dd9ff26cb631 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 19 Jul 2019 12:42:22 +0200 Subject: [PATCH 1/2] Add exhaustivity check tests --- .../src/dotty/tools/dotc/core/Types.scala | 6 +++ .../tools/dotc/transform/patmat/Space.scala | 4 +- tests/patmat/t11283.scala | 17 +++++++++ tests/patmat/t11457.scala | 13 +++++++ tests/patmat/t11603.scala | 5 +++ tests/patmat/t11620.check | 1 + tests/patmat/t11620.scala | 38 +++++++++++++++++++ tests/patmat/t9809.check | 2 + tests/patmat/t9809.scala | 9 +++++ 9 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 tests/patmat/t11283.scala create mode 100644 tests/patmat/t11457.scala create mode 100644 tests/patmat/t11603.scala create mode 100644 tests/patmat/t11620.check create mode 100644 tests/patmat/t11620.scala create mode 100644 tests/patmat/t9809.check create mode 100644 tests/patmat/t9809.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index eb22e1bb682d..864ff27555a1 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1020,6 +1020,12 @@ object Types { case _ => this } + /** Widen singleton type modulo constant types */ + final def widenNonConstant(implicit ctx: Context): Type = this match { + case _: ConstantType => this + case _ => widen + } + /** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */ final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match { case tp: ExprType => tp.resultType.widenIfUnstable diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index df0fc59ae925..6e7380e3b745 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -725,7 +725,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def checkExhaustivity(_match: Match): Unit = { val Match(sel, cases) = _match - val selTyp = sel.tpe.widen.dealias + val selTyp = sel.tpe.widenNonConstant.dealias if (!exhaustivityCheckable(sel)) return @@ -756,7 +756,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def checkRedundancy(_match: Match): Unit = { val Match(sel, cases) = _match - val selTyp = sel.tpe.widen.dealias + val selTyp = sel.tpe.widenNonConstant.dealias if (!redundancyCheckable(sel)) return diff --git a/tests/patmat/t11283.scala b/tests/patmat/t11283.scala new file mode 100644 index 000000000000..585a6b0bc6be --- /dev/null +++ b/tests/patmat/t11283.scala @@ -0,0 +1,17 @@ +sealed trait Color +case object Red extends Color +case object Blue extends Color +case object Green extends Color + +class Test { + + val R: Red.type = Red + val B: Blue.type = Blue + val G: Green.type = Green + + def go(c: Color): Int = c match { + case R => 0 + case G => 1 + case B => 2 + } +} \ No newline at end of file diff --git a/tests/patmat/t11457.scala b/tests/patmat/t11457.scala new file mode 100644 index 000000000000..e24a8dcc688b --- /dev/null +++ b/tests/patmat/t11457.scala @@ -0,0 +1,13 @@ +sealed abstract class Foo(val a: String) + +object Foo { + def unapply(foo: Foo): Some[String] = + Some(foo.a) +} + +class Issue11457 { + val root: PartialFunction[Foo, Boolean] = { + case Foo("a") => true + case Foo("b") => false + } +} diff --git a/tests/patmat/t11603.scala b/tests/patmat/t11603.scala new file mode 100644 index 000000000000..d8b44d320381 --- /dev/null +++ b/tests/patmat/t11603.scala @@ -0,0 +1,5 @@ +class C { + def m(x: true) = x match { + case true => println("the one true path") + } +} diff --git a/tests/patmat/t11620.check b/tests/patmat/t11620.check new file mode 100644 index 000000000000..ee18b987721e --- /dev/null +++ b/tests/patmat/t11620.check @@ -0,0 +1 @@ +36: Pattern Match Exhaustivity: B(A2(_, _)) diff --git a/tests/patmat/t11620.scala b/tests/patmat/t11620.scala new file mode 100644 index 000000000000..cc20696cd387 --- /dev/null +++ b/tests/patmat/t11620.scala @@ -0,0 +1,38 @@ +sealed trait A[+T] + case class A1[+T](t : T ) extends A[T] + case class A2[+T](t1: T, t2: T) extends A[T] + +sealed trait B[+T] { + type AA[+U] <: A[U] + def a: AA[T] +} +object B { + type Aux[+_A[+_], +T] = B[T] { type AA[+U] <: _A[U] } + object Aux { + def unapply[_A[+U] <: A[U], T](b: Aux[_A, T]): Some[_A[T]] = Some(b.a) + } + + def apply[_A[+U] <: A[U], T](_a: _A[T]): Aux[_A, T] = + new B[T] { type AA[+U] = _A[U] ; val a: _A[T] = _a } + + def unapply[T](b: B[T]): Some[b.AA[T]] = Some(b.a) +} + +def foo[T](b: B[T]) = b match { + case B(A1(t)) ⇒ t + case B(A2(t, _)) ⇒ t +} + +def foo2[_A[+U] <: A[U], T](b: B.Aux[_A, T]) = b match { + case B.Aux(a @ A1(_ )) ⇒ a.t + case B.Aux(a @ A2(_, _)) ⇒ a.t1 // 👎 (false-positive): unreachable code +} + +def foo3[_A[+U] <: A[U], T](b: B.Aux[_A, T]) = b match { + case B.Aux(a: A1[T]) ⇒ a.t + case B.Aux(a: A2[T]) ⇒ a.t1 // 👎 (false-positive): unreachable code +} + +def foo4[T](b: B[T]) = b match { + case B(A1(t)) ⇒ t // 👎 (false-negative): incomplete match +} diff --git a/tests/patmat/t9809.check b/tests/patmat/t9809.check new file mode 100644 index 000000000000..ff3fb4c63f64 --- /dev/null +++ b/tests/patmat/t9809.check @@ -0,0 +1,2 @@ +3: Pattern Match Exhaustivity: (_, _) +7: Pattern Match Exhaustivity: (_, _) diff --git a/tests/patmat/t9809.scala b/tests/patmat/t9809.scala new file mode 100644 index 000000000000..45d4946cdbe4 --- /dev/null +++ b/tests/patmat/t9809.scala @@ -0,0 +1,9 @@ +object Example { + val op1: (Any, Any) => Unit = { + case (_, b: Int) => + } + + val op2: (Unit, Any) => Unit = { + case (_, b: Int) => + } +} From ee18ae7db37060d3f66cba966e236efd62c950b2 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 19 Jul 2019 13:36:17 +0200 Subject: [PATCH 2/2] Revert support for t11603 - it's not a real use case and it's hard to imagine one - it does not work if the scrutinee and case are both singletons --- compiler/src/dotty/tools/dotc/core/Types.scala | 6 ------ compiler/src/dotty/tools/dotc/transform/patmat/Space.scala | 4 ++-- tests/patmat/{t11603.scala => t11603.scala.bak} | 0 3 files changed, 2 insertions(+), 8 deletions(-) rename tests/patmat/{t11603.scala => t11603.scala.bak} (100%) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 864ff27555a1..eb22e1bb682d 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1020,12 +1020,6 @@ object Types { case _ => this } - /** Widen singleton type modulo constant types */ - final def widenNonConstant(implicit ctx: Context): Type = this match { - case _: ConstantType => this - case _ => widen - } - /** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */ final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match { case tp: ExprType => tp.resultType.widenIfUnstable diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 6e7380e3b745..df0fc59ae925 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -725,7 +725,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def checkExhaustivity(_match: Match): Unit = { val Match(sel, cases) = _match - val selTyp = sel.tpe.widenNonConstant.dealias + val selTyp = sel.tpe.widen.dealias if (!exhaustivityCheckable(sel)) return @@ -756,7 +756,7 @@ class SpaceEngine(implicit ctx: Context) extends SpaceLogic { def checkRedundancy(_match: Match): Unit = { val Match(sel, cases) = _match - val selTyp = sel.tpe.widenNonConstant.dealias + val selTyp = sel.tpe.widen.dealias if (!redundancyCheckable(sel)) return diff --git a/tests/patmat/t11603.scala b/tests/patmat/t11603.scala.bak similarity index 100% rename from tests/patmat/t11603.scala rename to tests/patmat/t11603.scala.bak