Skip to content

Fix reachability of unapplySeq of non-List sequences #14112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/patmat/Space.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
12 changes: 6 additions & 6 deletions tests/neg-custom-args/fatal-warnings/i8711.check
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tests/patmat/i13485.check
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
11: Match case Unreachable
16: Match case Unreachable
2 changes: 1 addition & 1 deletion tests/patmat/i13485.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions tests/patmat/i13931.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 _ => }