From ebc5819656253a598e5a024a6df9ac6a7ff294bc Mon Sep 17 00:00:00 2001 From: Dale Wijnand Date: Tue, 14 Dec 2021 18:22:24 +0000 Subject: [PATCH] Fix reachability of unapplySeq of non-List sequences --- .../dotty/tools/dotc/transform/patmat/Space.scala | 14 +++++++++----- .../dotc/transform/PatmatExhaustivityTest.scala | 2 +- tests/neg-custom-args/fatal-warnings/i8711.check | 12 ++++++------ tests/patmat/i13485.check | 1 + tests/patmat/i13485.scala | 2 +- tests/patmat/i13931.scala | 3 +++ 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index 84345f095ad9..af7083f66f77 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -371,10 +371,15 @@ class SpaceEngine(using Context) extends SpaceLogic { val fun1 = funPart(fun) val funRef = fun1.tpe.asInstanceOf[TermRef] if (fun.symbol.name == nme.unapplySeq) - if (fun.symbol.owner == scalaSeqFactoryClass) + val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos) + if (fun.symbol.owner == scalaSeqFactoryClass && scalaListType.appliedTo(elemTp) <:< pat.tpe) + // The exhaustivity and reachability logic already handles decomposing sum types (into its subclasses) + // and product types (into its components). To get better counter-examples for patterns that are of type + // List (or a super-type of list, like LinearSeq) we project them into spaces that use `::` and Nil. + // Doing so with a pattern of `case Seq() =>` with a scrutinee of type `Vector()` doesn't work because the + // space is then discarded leading to a false positive reachability warning, see #13931. projectSeq(pats) else { - val (arity, elemTp, resultTp) = unapplySeqInfo(fun.tpe.widen.finalResultType, fun.srcPos) if (elemTp.exists) Prod(erase(pat.tpe.stripAnnots, isValue = false), funRef, projectSeq(pats) :: Nil) else @@ -959,9 +964,8 @@ class SpaceEngine(using Context) extends SpaceLogic { if prev == Empty && covered == Empty then // defer until a case is reachable deferred ::= pat else { - // FIXME: These should be emitted, but reverted for i13931 - //for (pat <- deferred.reverseIterator) - // report.warning(MatchCaseUnreachable(), pat.srcPos) + for (pat <- deferred.reverseIterator) + report.warning(MatchCaseUnreachable(), pat.srcPos) if pat != EmptyTree // rethrow case of catch uses EmptyTree && isSubspace(covered, prev) then { diff --git a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala index 3d877a46558f..7d9c77a439bf 100644 --- a/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala +++ b/compiler/test/dotty/tools/dotc/transform/PatmatExhaustivityTest.scala @@ -66,7 +66,7 @@ class PatmatExhaustivityTest { .filter(f => f.extension == "scala" || f.isDirectory) .filter { f => val path = if f.isDirectory then f.path + "/" else f.path - path.contains(Properties.testsFilter.getOrElse("")) + Properties.testsFilter.getOrElse("").split(',').exists(path.contains) } .map(f => if f.isDirectory then compileDir(f.jpath) else compileFile(f.jpath)) diff --git a/tests/neg-custom-args/fatal-warnings/i8711.check b/tests/neg-custom-args/fatal-warnings/i8711.check index 0abda7a77ed6..0035af0755d4 100644 --- a/tests/neg-custom-args/fatal-warnings/i8711.check +++ b/tests/neg-custom-args/fatal-warnings/i8711.check @@ -1,8 +1,8 @@ --- Error: tests/neg-custom-args/fatal-warnings/i8711.scala:7:9 --------------------------------------------------------- +-- [E030] Match case Unreachable Error: tests/neg-custom-args/fatal-warnings/i8711.scala:7:9 --------------------------- 7 | case x: B => x // error: this case is unreachable since class A is not a subclass of class B - | ^ - | this case is unreachable since type A and class B are unrelated --- Error: tests/neg-custom-args/fatal-warnings/i8711.scala:12:9 -------------------------------------------------------- + | ^^^^ + | Unreachable case +-- [E030] Match case Unreachable Error: tests/neg-custom-args/fatal-warnings/i8711.scala:12:9 -------------------------- 12 | case x: C => x // error - | ^ - | this case is unreachable since type A | B and class C are unrelated + | ^^^^ + | Unreachable case diff --git a/tests/patmat/i13485.check b/tests/patmat/i13485.check index f05a4c6c0788..f9d066905a86 100644 --- a/tests/patmat/i13485.check +++ b/tests/patmat/i13485.check @@ -1 +1,2 @@ +11: Match case Unreachable 16: Match case Unreachable diff --git a/tests/patmat/i13485.scala b/tests/patmat/i13485.scala index 2c8ef5547704..72cc3a3d2cd3 100644 --- a/tests/patmat/i13485.scala +++ b/tests/patmat/i13485.scala @@ -8,7 +8,7 @@ sealed trait Foo class Bar def test1(bar: Bar) = bar match - case _: Foo => 1 // FIXME: this is unreachable, but reverted for i13931 + case _: Foo => 1 case _: Bar => 2 def test2(bar: Bar) = bar match diff --git a/tests/patmat/i13931.scala b/tests/patmat/i13931.scala index b6c8ba6accd8..0d8d9eb9dcd3 100644 --- a/tests/patmat/i13931.scala +++ b/tests/patmat/i13931.scala @@ -2,3 +2,6 @@ class Test: def test = Vector() match case Seq() => println("empty") case _ => println("non-empty") + + def test2 = IndexedSeq() match { case IndexedSeq() => case _ => } + def test3 = IndexedSeq() match { case IndexedSeq(1) => case _ => }