Open
Description
Compiler version
3.2.2
Motivation
The following code compiles for type-parameter GADTs:
- case classes
trait A[T]
case class B() extends A[Int]
case class C() extends A[String]
// compiles
def foo11[T](a: A[T]): T =
a match
case B() => 1
case C() => "a"
// compiles
def foo12[T](a: A[T]): T =
a match
case _: B => 1
case _: C => "a"
- objects
trait A[T]
object B extends A[Int]
object C extends A[String]
// compiles
def foo21[T](a: A[T]): T =
a match
case B => 1
case C => "a"
// compiles
def foo22[T](a: A[T]): T =
a match
case _: B.type => 1
case _: C.type => "a"
But none of the following compiles for type-member GADTs:
Minimized code
- case classes
/*sealed*/ trait A:
type T
/*final*/ case class B() extends A:
override type T = Int
/*final*/ case class C() extends A:
override type T = String
// doesn't compile
def foo31(a: A): a.T =
a match
case B() => 1 // Found: (1 : Int), Required: a.T
case C() => "a" // Found: ("a" : String), Required: a.T
// doesn't compile
def foo32(a: A): a.T =
a match
case _: B => 1 // Found: (1 : Int), Required: a.T
case _: C => "a" // Found: ("a" : String), Required: a.T
// doesn't compile (but in 2.13.10 it compiles for final B, C)
def foo311[_T](a: A {type T = _T}): _T =
a match
case B() => 1 // Found: (1 : Int), Required: _T
case C() => "a" // Found: ("a" : String), Required: _T
// doesn't compile (but in 2.13.10 it compiles for final B, C)
def foo321[_T](a: A {type T = _T}): _T =
a match
case _: B => 1 // Found: (1 : Int), Required: _T
case _: C => "a" // Found: ("a" : String), Required: _T
- (case) objects
/*sealed*/ trait A:
type T
/*final*/ /*case*/ object B extends A:
override type T = Int
/*final*/ /*case*/ object C extends A:
override type T = String
// doesn't compile
def foo41(a: A): a.T =
a match
case B => 1 // Found: (1 : Int), Required: a.T
case C => "a" // Found: ("a" : String), Required: a.T
// doesn't compile
def foo42(a: A): a.T =
a match
case _: B.type => 1 // Found: (1 : Int), Required: a.T
case _: C.type => "a" // Found: ("a" : String), Required: a.T
// doesn't compile (but in 2.13.10 it compiles)
def foo411[_T](a: A {type T = _T}): _T =
a match
case B => 1 // Found: (1 : Int), Required: _T
case C => "a" // Found: ("a" : String), Required: _T
// doesn't compile (but in 2.13.10 it compiles for final B, C)
def foo421[_T](a: A {type T = _T}): _T =
a match
case _: B.type => 1 // Found: (1 : Int), Required: _T
case _: C.type => "a" // Found: ("a" : String), Required: _T
In Scala 3 the behavior doesn't depend on whether A
is sealed, B
, C
are final/non-final case classes/objects/case objects
Expectation
Code snippets 3-4 should compile too. Or is it intended difference between type parameters and type members? (Functional dependencies?)
Workarounds
- match types
type Foo[X <: A] = X match
case B => Int
case C => String
def foo[X <: A](a: X): Foo[A] = a match
case _: B => 1
case _: C => "a"
- inlining and implicit hints
inline def foo(a: A): a.T = inline a match
case _: B => summonFrom {
case _: (Int =:= a.T) => 1
}
case _: C => summonFrom {
case _: (String =:= a.T) => "a"
}
- type classes