Skip to content

Missing unreachable warning/error in pattern matching case with Union Type #13277

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

Open
dos65 opened this issue Aug 10, 2021 · 6 comments
Open

Comments

@dos65
Copy link
Contributor

dos65 commented Aug 10, 2021

3.0.2-RC1

Minimized code

trait A
trait B
def foo(x: String): Unit =
  x match {
    case aOrB: (A | B) =>
  }

Output

It compiles without any error or warning.

Expectation

Compilation should fails as it does for the following:

trait A
trait B
def foo(x: String): Unit =
  x match {
    case aOrB: A =>
             ^
             this case is unreachable since type String is not a subclass of trait A
  }
@dwijnand
Copy link
Member

That unreachable warning is actually emitted during Erasure, not pattern matching (not sure why). The reason that this doesn't emit any warnings from the pattern matcher is because, by design, transitively non-sealed types aren't check (and neither A nor B are sealed). So should that reachability warning also be suppressed?

If you enable the -Ycheck-all-patmat then you do get a warning:

-- [E029] Pattern Match Exhaustivity Warning: tests/patmat/i13277.scala:5:2 ----
5 |  x match {
  |  ^
  |  match may not be exhaustive.
  |
  |  It would fail on pattern case: _: String

longer explanation available when compiling with `-explain`
1 warning found

Should that also give a reachability warning? Should it only give a reachability warning? I'm not sure - I kind of feel that at least one warning is enough.

@dwijnand dwijnand self-assigned this Aug 10, 2021
@dos65
Copy link
Contributor Author

dos65 commented Aug 10, 2021

@dwijnand even with enabled -Ycheck-all-patmat there is a difference in behaviour with/without union type if pat-mat is exhaustive

trait A
trait B

def foo(x: String): Unit =
  x match {
    case aOrB: A => // [error]: this case is unreachable since type String is not a subclass of trait A
    case _: String => 
  }

def foo2(x: String): Unit =
  x match {
    case aOrB: (A | B) => // no warning/error
    case _: String => 
  }

@dwijnand
Copy link
Member

Yeah, I guess we can look to making foo2 warn if all components of the union are unreachable.

@Swoorup
Copy link

Swoorup commented Aug 10, 2021

Why can't the compiler do this check before the erasure? I was looking forward to scala compiler stopping me from abusing union types 😅
I guess this is also related: #12805

@dwijnand
Copy link
Member

Should that also give a reachability warning?

I think I saw this before (because Scala 2 does have this logic): Scala 3 isn't taking into consideration that String (the scrutinee type) is final and therefore it could never have a subclass that mixes in trait A.

@dwijnand
Copy link
Member

The cause here is that Erasure has a flagUnrelated flag that is being enabled for type tests with disjunctions:

          case OrType(tp1, tp2) =>
            evalOnce(expr) { e =>
              transformTypeTest(e, tp1, flagUnrelated = false)
                .or(transformTypeTest(e, tp2, flagUnrelated = false))
            }

Sometimes only one part is unrelated, so it doesn't want to warn on those.

Part of the problem is the code reuse: both transforming and warning. I couldn't find a straightforward way to delay the warning so I'm parking this.

@dwijnand dwijnand removed their assignment Sep 27, 2021
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

4 participants