Skip to content
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

Pattern matching tuples fails to produce exhaustivity warning #10019

Closed
scabug opened this issue Nov 3, 2016 · 15 comments
Closed

Pattern matching tuples fails to produce exhaustivity warning #10019

scabug opened this issue Nov 3, 2016 · 15 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Nov 3, 2016

This feels like a bug?

Compare:

object Bug {
  sealed case class Foo(e: Option[Int])

  def loop(t: Foo): Nothing = t match {
    case Foo(Some(_)) => ???
  }
}
/*
match may not be exhaustive.
[warn] It would fail on the following input: Foo(None)
[warn]   def loop(t: Foo): Nothing = t match {
[warn]                               ^
*/

to this, which is also not exhaustive but produces no warning:

object Bug {
  sealed case class Foo(e: Option[Int])

  def loop(s: Foo, t: Foo): Nothing = (s,t) match {
    case (Foo(Some(_)), _) => ???
  }
}

I know Tuple2 isn't sealed, but that doesn't seem to explain it (problem occurs even using a custom sealed replacement for Tuple2, plus the stdlib tuples participate successfully in match exhaustivity checks in some other contexts).

Any ideas?

@scabug
Copy link
Author

scabug commented Nov 3, 2016

Imported From: https://issues.scala-lang.org/browse/SI-10019?orig=1
Reporter: @refried
Affected Versions: 2.11.8, 2.12.0
See #6450

@scabug
Copy link
Author

scabug commented Nov 7, 2016

@SethTisue said:
offhand, it looks similar to #6450. does it seem like a duplicate to you? or so close that it ought to just be a comment on that ticket? or is this actually quite distinct?

@scabug
Copy link
Author

scabug commented Nov 7, 2016

@refried said (edited on Nov 7, 2016 8:37:19 PM UTC):
Hmm, I don't claim to be an expert, but I think they are quite distinct. #6450 shows a false-positive in a seemingly yucky match involving wildcard types and .isInstanceOf and working directly with the Some class instead of Option class.

#10019 (this ticket) shows a false-negative in a seemingly very simple case.

@scabug
Copy link
Author

scabug commented Nov 7, 2016

@SethTisue said:
doh, I should have noticed the other one is false-positive. okay, thanks

@scabug
Copy link
Author

scabug commented Nov 7, 2016

@SethTisue said:
@adriaanm hesitating before just throwing this in the "Backlog" bucket, what do you think?

@scabug
Copy link
Author

scabug commented Nov 11, 2016

@refried said:
If the goal is to get someone to take a look at it, then why not :-D

@scabug
Copy link
Author

scabug commented Nov 11, 2016

@refried said (edited on Nov 11, 2016 10:43:07 PM UTC):
I found this surprising too:

def foo(t1: List[T], t2: List[T]) = (t1, t2) match {
  case (Nil, Nil) => ()
}

This code issues a warning:

[warn] It would fail on the following inputs: (List(_), List(_)), (List(_), Nil), (Nil, List(_))}

But this code issues no warnings:

def foo(t1: List[T], t2: List[T]) = (t1, t2) match {
  case (Nil, Nil) => ()
  case (List(_), List(_)) => ()
}

even though there are still missing cases.

Do you think it's related?

@scabug
Copy link
Author

scabug commented Nov 11, 2016

@refried said:
I'm trying to write a more complicated set of match expressions and I would love to feel confident that I wasn't forgetting anything.

@aklosslp
Copy link

Here's another similar case:

    val x: (Option[String], Option[String])  = (None, None)
    x match {
      case (Some(str), None) => println(s"$str, None")
      case (None, Some(str)) => println(s"None, $str")
      case (Some(strA), Some(strB)) if strA == strB => ???
    }

Note that removing the if strA == strB does correctly emit the warning.

@SethTisue
Copy link
Member

@aklosslp that looks like #5365 to me

@aklosslp
Copy link

aklosslp commented Apr 3, 2018

Yep I agree.

@dwijnand
Copy link
Member

I know Tuple2 isn't sealed

It's final which is even better! So that should not matter.

@dwijnand
Copy link
Member

But this code issues no warnings:

def foo(t1: List[T], t2: List[T]) = (t1, t2) match {
  case (Nil, Nil) => ()
  case (List(_), List(_)) => ()
}

even though there are still missing cases.

Btw, that's because that's using List#unapplySeq, which is #9232 (and specifically mentions List). It's particularly bad because ::'s toString returns List(_) instead of _ :: _.

(I'm going to consider this issue specific to tuples.)

@dwijnand dwijnand changed the title pattern matcher fails to produce exhaustivity warning Pattern matching tuples fails to produce exhaustivity warning Jul 24, 2020
@dwijnand dwijnand self-assigned this Aug 2, 2020
@dwijnand dwijnand added the has PR label Aug 2, 2020
@dwijnand
Copy link
Member

dwijnand commented Aug 2, 2020

I found this surprising too:

def foo(t1: List[T], t2: List[T]) = (t1, t2) match {
  case (Nil, Nil) => ()
}

This code issues a warning:

[warn] It would fail on the following inputs: (List(_), List(_)), (List(_), Nil), (Nil, List(_))}

But this code issues no warnings:

def foo(t1: List[T], t2: List[T]) = (t1, t2) match {
  case (Nil, Nil) => ()
  case (List(_), List(_)) => ()
}

even though there are still missing cases.

Relevant commentary in the pattern matcher:

      // special-case: interpret pattern `List()` as `Nil`
      // TODO: make it more general List(1, 2) => 1 :: 2 :: Nil  -- not sure this is a good idea...
      private val rewriteListPattern: PartialFunction[TreeMaker, Prop] = {
        case p @ ExtractorTreeMaker(_, _, testedBinder)
          if testedBinder.tpe.typeSymbol == ListClass && p.checkedLength == Some(0) =>
            uniqueEqualityProp(binderToUniqueTree(p.prevBinder), unique(Ident(NilModule) setType NilModule.tpe))
      }

😄

I'm going to leave out this part, as, as I said, it's more generally covered by #9232.

@lrytz lrytz closed this as completed Aug 18, 2020
@lrytz lrytz added this to the 2.12.13 milestone Aug 18, 2020
@dwijnand
Copy link
Member

Fixed in scala/scala#9147.

finaglehelper pushed a commit to twitter/util that referenced this issue Jan 29, 2021
Problem

Supporting latest https://github.com/scala/scala/releases/tag/v2.12.13 includes
a feature change in the scala compiler that will fail to compile when case
matching is not exhaustive on tuples.

Issues
- scala/bug#10680
- scala/bug#10373
- scala/bug#10019

Resolved
- scala/scala#9163
- scala/scala#9147

Solution
Add `case _ => throw new MatchError` to case matching statement that is
non-exhaustive

JIRA Issues: SCALA-25

Differential Revision: https://phabricator.twitter.biz/D609046
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants