Skip to content

Conversation

@retronym
Copy link
Member

Rather than disabling the analysis altogether, rewrite
guard to true (by default) or to false under a new
compiler option.

The default option errs on the side of avoiding spurious
warnings where the guard writer knows better. However,
there was a convincing argument made in the ticket that
in these cases it would not be to onerous to require
the code to be rewritten from

case P if g1 =>
case P if g2 =>

to:

case P if g1 =>
case P       =>

Or to:

(scrut: @unchecked) match {
  case P if g1 =>
  case P if g2 =>

So perhaps we can turn on the strict version by
default, after running against the community build
as a sanity check.

Extractor patterns had the same limitation as guards.
This commit also enables the analysis for them.

@scala-jenkins scala-jenkins added this to the 2.12.0-M4 milestone Jan 29, 2016
@retronym
Copy link
Member Author

TODO:

@xuwei-k
Copy link
Contributor

xuwei-k commented Jan 29, 2016

s/exhautivity/exhaustivity ?

@retronym retronym changed the title SI-5365 Enable exhautivity analysis of matches with guards SI-5365 Enable exhaustivity analysis of matches with guards Jan 29, 2016
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This warning message is imprecise, it should technically say Some(x forSome x not in Extractor(x)).

@retronym
Copy link
Member Author

I just found another hole in our exhaustivity checking.

class Test {
  def foo {
    case class Foo(a: Any)
    val xs: List[Foo] = Nil
    xs match {
      case Foo(a) :: Nil =>
    }
  }
}

The constructor pattern Foo gets translated into a call to unapplySeq, rather than to a extractor of the case class product elements. This hampers exhaustivity checking (disabled it completely prior to this PR, and forces us to make an assumption even after this PR). It also means that the code is less efficient, and needlessly allocates a tuple.

This root cause of this was fixed recently as #4862 (which isn't merged yet to the 2.12.x branch).

Rather than disabling the analysis altogether, rewrite
guard to `true` (by default) or to `false` under a new
compiler option.

The default option errs on the side of avoiding spurious
warnings where the guard writer knows better. However,
there was a convincing argument made in the ticket that
in these cases it would not be to onerous to require
the code to be rewritten from

    case P if g1 =>
    case P if g2 =>

to:

    case P if g1 =>
    case P       =>

Or to:

    (scrut: @unchecked) match {
      case P if g1 =>
      case P if g2 =>

So perhaps we can turn on the strict version by
default, after running against the community build
as a sanity check.

Extractor patterns had the same limitation as guards.
This commit also enables the analysis for them in the
same way as done for guards. However, this still not
done for `unapplySeq` extractors, as the counter example
generator can't handle these yet.
@adriaanm adriaanm self-assigned this Feb 9, 2016
@durban
Copy link

durban commented Feb 22, 2016

I understand this still doesn't issue a warning if the type of scrut is not a sealed trait (unless I'm missing something). That is, code like this still compiles without a warning:

trait Z
final case class Q(i: Int) extends Z
def f(z: Z) = z match {
  case Q(_) =>
}

Would it be possible to get a warning in cases like this too? I realize, that this is even more strict than the -Xstrict-patmat-analysis in this PR, so maybe behind an -Xlint option?

Also, after playing a bit more with this code, the warnings for extractors seem to behave somewhat surprisingly. For example, code like this compiles without any warnings:

sealed trait T
final case class C(i: Int) extends T
final case class D(i: Int) extends T

object X {
  def unapply(t: T): Option[Int] = None
}

def g(t: T): Unit = t match {
  case X(_) =>
}

However, if I put in another case, thus increasing (or at least not decreasing) the exhaustiveness of the match, I get an (expected) warning:

def g(t: T): Unit = t match {
  case X(_) =>
  case C(_) =>
}

To be clear, the (to me) surprising thing is the absence of the warning in the first case. Is this behavior intentional?

@adriaanm
Copy link
Contributor

Let's take another look at this after M5. Scheduling for RC1.

@adriaanm adriaanm modified the milestones: 2.12.0-RC1, 2.12.0-M5 May 31, 2016
@retronym
Copy link
Member Author

I don't have time to push this one forward right now, so I'm going to close the PR for now.

@sellout
Copy link
Contributor

sellout commented Nov 29, 2016

I’d like to pick this up, if it’s ok with @retronym.

My own opinions: it’d be nice to make “false” the default (@adriaanm – how likely is that?). Also, we should warn whenever the checker gives up (which I think would cover @durban’s X(_) case?) and I like the -Xlint idea for the unsealed trait case.

@adriaanm
Copy link
Contributor

Great! I can be convinced of whatever strategy results in the best experience for the majority of Scala programmers.

approx.fullRewrite.applyOrElse[TreeMaker, Prop](tm, {
case BodyTreeMaker(_, _) => True // irrelevant -- will be discarded by symbolCase later
case GuardTreeMaker(_) => if (strict) False else True
case ExtractorTreeMaker(extractor, _, _) if extractor.symbol.name != nme.unapplySeq =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@retronym I thought the guard here was because of SI-9567 (fixed in #4862, which seems to have been merged into 2.12.x at this point). However, removing it in order to get the correct repeatedExtractor warnings causes other tests to fail (e.g., run/patmatnew.scala:58, which is an exhaustive match on List[Int]).

Wondering if you think there’s a middle ground that can be achieved, or if the repeatedExtractor warnings are a bit too ambitious (at least for me).

@LogicalTime
Copy link

We should at least let this be available behind a compiler flag. Without this people will avoid using guards so they can be sure of the exhaustive warning/compile error.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants