# "unreachable code" false-positives, "match not exhaustive" false-negative warnings when matching on HKTs #11620

Open
opened this issue Jul 8, 2019 · 6 comments

Projects
None yet
4 participants

### ryan-williams commented Jul 8, 2019

 scalafiddle; repros on 2.12.8 and 2.13.0 ```sealed trait A[+T] case class A1[+T](t : T ) extends A[T] case class A2[+T](t1: T, t2: T) extends A[T] sealed trait B[+T] { type AA[+U] <: A[U] def a: AA[T] } object B { type Aux[+_A[+_], +T] = B[T] { type AA[+U] <: _A[U] } object Aux { def unapply[_A[+U] <: A[U], T](b: Aux[_A, T]): Option[_A[T]] = Some(b.a) } def apply[_A[+U] <: A[U], T](_a: _A[T]): Aux[_A, T] = new B[T] { type AA[+U] = _A[U] ; val a: _A[T] = _a } def unapply[T](b: B[T]): Option[b.AA[T]] = Some(b.a) } def foo[T](b: B[T]) = b match { case B(A1(t)) ⇒ t case B(A2(t, _)) ⇒ t } def foo2[_A[+U] <: A[U], T](b: B.Aux[_A, T]) = b match { case B.Aux(a @ A1(_ )) ⇒ a.t case B.Aux(a @ A2(_, _)) ⇒ a.t1 // 👎 (false-positive): unreachable code } def foo3[_A[+U] <: A[U], T](b: B.Aux[_A, T]) = b match { case B.Aux(a: A1[T]) ⇒ a.t case B.Aux(a: A2[T]) ⇒ a.t1 // 👎 (false-positive): unreachable code } def foo4[T](b: B[T]) = b match { case B(A1(t)) ⇒ t // 👎 (false-negative): incomplete match } val b1: B.Aux[A1, Int] = B(A1(111)) val b2: B.Aux[A2, Int] = B(A2(222, 333)) println(s"\${foo (b1)} \${foo (b2)}") println(s"\${foo2(b1)} \${foo2(b2)}") println(s"\${foo3(b1)} \${foo3(b2)}")``` Compilation generates false-positive warnings as annotated above: ``````[warn] case B.Aux(a @ A2(_, _)) ⇒ a.t1 // 👎 (false-positive): unreachable code [warn] ^ [warn] case B.Aux(a: A2[T]) ⇒ a.t1 // 👎 (false-positive): unreachable code [warn] ^ `````` (scalafiddle doesn't display these for some reason) All three `println`s work as expected, reaching the "unreachable code": ``````111 222 111 222 111 222 `````` The compiler also fails to flag the inexhaustive match in `foo4`.

### joroKr21 commented Jul 18, 2019 • edited

 About `foo4`, I think exhaustiveness is not checked when using custom `unapply` methods 🤔

### smarter commented Jul 18, 2019

 I think exhaustiveness is not checked when using custom unapply methods thinking You can mark a custom unapply as irrefutable by making its result type `Some[...]` instead of `Option[...]`: ` def unapply[T](b: B[T]): Some[b.AA[T]] = Some(b.a)`

### joroKr21 commented Jul 18, 2019

 This doesn't seem to work in Scala 2. Still doesn't warn: ``` object A { def unapply(x: Int): Some[Int] = Some(x) } 42 match { case A(5) => 5 }```

### smarter commented Jul 18, 2019

 It helps in cases like this: ```scala> object A { def unapply(x: Int): Some[Int] = Some(x) } defined object A scala> object B { def unapply(x: Int): Option[Int] = Some(x) } defined object B scala> List(1) match { case A(x) :: xs => } :13: warning: match may not be exhaustive. It would fail on the following input: Nil List(1) match { case A(x) :: xs => } ^ scala> List(1) match { case B(x) :: xs => }```

### joroKr21 commented Jul 18, 2019

 Ah so it works if the custom `unapply` is a leaf but not when it's a branch in the pattern match. Well that's confusing 😕

### joroKr21 commented Jul 19, 2019 • edited

 Related? #11457