Problem:
In one of my previous question I provided an example with the following pattern-matching
class A
class B
sealed trait Test {
type Meta
}
case class Test1() extends Test {
type Meta = A
}
case class Test2() extends Test {
type Meta = B
}
case class Info[T <: Test](t: T, m: T#Meta)
val t: Info[_ <: Test] = ???
t match {
case Info(t: Test1, a: Test1#Meta) =>
println("1")
case Info(t: Test2, a: Test2#Meta) =>
println("2")
}
This code compiles with the following warning:
match may not be exhaustive.
It would fail on the following inputs:
Info((x: _$2 forSome x not in (Test1, Test2)), (x: _$2#Meta forSome x not in (A, B))),
Info((x: _$2 forSome x not in (Test1, Test2)), ??), Info((x: _$2 forSome x not in (Test1, Test2)), A()),
Info((x: _$2 forSome x not in (Test1, Test2)), B()), Info(??, (x: _$2#Meta forSome x not in (A, B))),
Info(Test1(), (x: _$2#Meta forSome x not in (A, B))),
Info(Test1(), B()), Info(Test2(), (x: _$2#Meta forSome x not in (A, B))),
Info(Test2(), A())
As Oleg Pyzhcov greatly explined in their answer the it is indeed possible to construct a value which would fail to match.
My solution:
So the solution I currently think of is to prohibit such object creation with a simple macro. Here it is:
object Info {
def apply[T <: Test](t: T, m: T#Meta): Info[T] = macro applyImpl[T]
def applyImpl[T <: Test](c: blackbox.Context)(t: c.Expr[T], m: c.Expr[T#Meta]): c.Expr[Info[T]] = {
import c.universe._
val sourceType = weakTypeTag[T].tpe
if (!sourceType.typeSymbol.isClass || !sourceType.typeSymbol.isFinal) {
c.abort(
c.enclosingPosition,
s"Creating is allowed for specific types only"
)
}
//... create an object
}
}
With such apply
implementation it is not possible to construct such objects anymore.
QUESTION: Is it reliable to suppress the warning compiler raises when using such apply
implementation?