question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unions of Singletons unreachable literals not being checked in pattern match.

See original GitHub issue

The pattern match on the lhs side doesn’t exhaustively check if the pattern is of valid type for singletons of union.

In this example both 240 and 4H are not a valid subtype of both TimeframeN and Timeframe respectively, but compiler appears to compile without reporting type mismatch errors. It appears as if union type is widen’ed prematurely.

Compiler version

3.0.1-RC1

Minimized code

import scala.language.implicitConversions

type Timeframe = "1m" | "2m" | "1H"
type TimeframeN = 1 | 2 | 60

def manualConvertToN(tf: Timeframe): TimeframeN = tf match
  case "1m" => 1
  case "2m" => 2
  case "1H" => 60
  case "4H" => ??? // incorrect but compiles

given Conversion[Timeframe, TimeframeN] = 
  case "1m" => 1
  case "2m" => 2
  case "1H" => 60
  case "4H" => ??? // incorrect but compiles

given Conversion[TimeframeN, Timeframe] = 
  case 1 => "1m"
  case 2 => "2m"
  case 60 => "1H"
  case 240 => ??? // incorrect but compiles

Output

No errors on compilation

Expectation

Type mismatch errors. Hypothetical sample:

[error] -- [E007] Type Mismatch Error: /Users/swoorup.joshi/playground/test/src/main/scala/Main.scala:18:15
[error] 18 |  case "4H" => ???
[error]    |        ^^^
[error]    |        Found:    ("4H" : String)
[error]    |        Required: Timeframe

[error] -- [E007] Type Mismatch Error: /Users/swoorup.joshi/playground/test/src/main/scala/Main.scala:24:15
[error] 24 |  case "4H" => ???
[error]    |        ^^^
[error]    |        Found:    ("4H" : String)
[error]    |        Required: Timeframe

[error] -- [E007] Type Mismatch Error: /Users/swoorup.joshi/playground/test/src/main/scala/Main.scala:30:15
[error] 30 |  case 240 => ???
[error]    |       ^^^
[error]    |       Found:    (240 : Int)
[error]    |       Required: TimeframeN

Note that this doesn’t happen if the returning subtype is invalid. In such cases, compiler reports a type mismatch error correctly.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:10 (6 by maintainers)

github_iconTop GitHub Comments

1reaction
SethTisuecommented, Aug 18, 2021

Dale and I attempted a fix on the draft PR https://github.com/lampepfl/dotty/pull/13290, but our efforts may have stalled; see our recent remarks there.

1reaction
romanowskicommented, Aug 11, 2021

It seems that exclusivity checks does not pick up a case where additional singletons are provided.

scala> def foo(a: 1 | 2) = a match       // Compiles fine                                                                                                               
     |   case 1 => true
     |   case 2 => false
     |   case 4 => ???                          
     | 
def foo(a: 1 | 2): Boolean

scala> def bar(a: 1 | 2) = a match                                                                                                                       
     |   case 1 => true
     |   case 2 => false
     |   case _ => ???
     | 
4 |  case _ => ???
  |       ^
  |       Unreachable case

scala> def baz(a: 1 | 2) = a match                                                                                                                       
     |   case 1 => true
     |   case 2 => false
     |   case a if a < 0 => ???
     | 
4 |  case a if a < 0 => ???
  |       ^
  |       Unreachable case
Read more comments on GitHub >

github_iconTop Results From Across the Web

Match exhaustion warning on literal type · Issue #11603 · scala/bug ...
class C { def m(x: true) = x match { case true => println("the one true path") } ... Unions of Singletons unreachable...
Read more >
f# - Inconsistent behavior in pattern matching - Stack Overflow
Looking at the output in Reflector it seems like a bug. Incidentally, there are many ways to do the null check in a...
Read more >
Pattern matching | Internet Computer Home
Pattern matching is a language feature that makes it easy to both test and decompose structured data into its constituent parts.
Read more >
Exhaustive Union Matching in Python
A check on whether a given match is “exhaustive”, i.e., covers all possible inputs, is helpful to avoid bugs when the set of...
Read more >
Union Types - More Details - Scala Documentation
Exhaustivity checking​​ If the selector of a pattern match is a union type, the match is considered exhaustive if all parts of the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found