Closed
Description
Compiler version
3.6.4 (or 3.7-RC4)
Minimized example
object OpaqueScope:
opaque type Opaque[A, B] = Unit
trait Trait[A, B] {}
object OtherScope:
import OpaqueScope.*
type OpaqueLeft[Tp <: Tuple] = Tp match
case EmptyTuple => Nothing
case Opaque[l, _] *: _ => l
case _ *: rest => OpaqueLeft[rest]
type TraitLeft[Tp <: Tuple] = Tp match
case EmptyTuple => Nothing
case Trait[l, _] *: _ => l
case _ *: rest => TraitLeft[rest]
def leftO[Tp <: Tuple](x: OpaqueLeft[Tp]): Int = x.##
def leftT[Tp <: Tuple](x: TraitLeft[Tp]): Int = x.##
val traited = leftT[(Int, Trait[Char, Boolean], String)]('e')
val opaqued = leftO[(Int, Opaque[Char, Boolean], String)]('e')
Output
Found: ('e' : Char)
Required: Playground.OtherScope.OpaqueLeft[(Int,
Playground.OpaqueScope.Opaque[Char, Boolean], String)]
Note: a match type could not be fully reduced:
trying to reduce Playground.OtherScope.OpaqueLeft[(Int,
Playground.OpaqueScope.Opaque[Char, Boolean], String)]
failed since selector (Int, Playground.OpaqueScope.Opaque[Char, Boolean], String)
does not match case Playground.OpaqueScope.Opaque[l, _] *: _ => l
and cannot be shown to be disjoint from it either.
Therefore, reduction cannot advance to the remaining case
case _ *: rest => Playground.OtherScope.OpaqueLeft[rest]
Expectation
The compiler will treat type-level computations on types the same way, regardless of type, so it is irrelevant whether the type is opaque or not when doing a match type computation.
Alternatively, case Opaque[l, _] *: _ => ...
will fail to compile. If it is intrinsically unmatchable, it should complain when you try to match on it and not leave you wondering why two things of the same shape behave differently.
Because opaque types are the primary mechanism for zero-cost abstraction in Scala, the former is strongly preferred.