diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4ae31b2e05e8..70eebc750521 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -768,6 +768,7 @@ class Definitions { @tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply) @tu lazy val TypeTestClass: ClassSymbol = requiredClass("scala.reflect.TypeTest") + @tu lazy val TypeTest_unapply: Symbol = TypeTestClass.requiredMethod(nme.unapply) @tu lazy val TypeTestModule_identity: Symbol = TypeTestClass.companionModule.requiredMethod(nme.identity) @tu lazy val QuotedExprClass: ClassSymbol = requiredClass("scala.quoted.Expr") diff --git a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala index c791290584e7..86f15451fa2f 100644 --- a/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala +++ b/compiler/src/dotty/tools/dotc/transform/patmat/Space.scala @@ -265,8 +265,13 @@ trait SpaceLogic { case (Prod(tp1, _, _, false), Typ(tp2, _)) => if (isSubType(tp1, tp2)) Empty else a - case (Typ(tp1, _), Prod(tp2, _, _, false)) => - a // approximation + case (Typ(tp1, _), Prod(tp2, unappTp, params, false)) => + if unappTp.symbol == defn.TypeTest_unapply then + // The `TypeTest[S, T].unapply` covers all `T`s if the scrutinee is of type `S`. + // If the scrutinee is not of type `S`, then we would already have an unchecked warning. + minus(a, params.head) + else + a // approximation case (Prod(tp1, fun1, ss1, full), Prod(tp2, fun2, ss2, _)) => if (!isSameUnapply(fun1, fun2)) return a diff --git a/library/src/scala/reflect/TypeTest.scala b/library/src/scala/reflect/TypeTest.scala index 7d3cc9908cc4..6eef9c457768 100644 --- a/library/src/scala/reflect/TypeTest.scala +++ b/library/src/scala/reflect/TypeTest.scala @@ -1,7 +1,7 @@ package scala.reflect /** A `TypeTest[S, T] contains the logic needed to know at runtime if a value of - * type `S` can be downcasted to `T`. + * type `S` is an instance of `T`. * * If a pattern match is performed on a term of type `s: S` that is uncheckable with `s.isInstanceOf[T]` and * the pattern are of the form: @@ -12,7 +12,7 @@ package scala.reflect @scala.annotation.implicitNotFound(msg = "No TypeTest available for [${S}, ${T}]") trait TypeTest[-S, T] extends Serializable: - /** A TypeTest[S, T] can serve as an extractor that matches only S of type T. + /** A TypeTest[S, T] can serve as an extractor that matches if and only if S of type T. * * The compiler tries to turn unchecked type tests in pattern matches into checked ones * by wrapping a `(_: T)` type pattern as `tt(_: T)`, where `tt` is the `TypeTest[S, T]` instance. diff --git a/tests/pos-special/fatal-warnings/i12020.scala b/tests/pos-special/fatal-warnings/i12020.scala new file mode 100644 index 000000000000..93bd70290112 --- /dev/null +++ b/tests/pos-special/fatal-warnings/i12020.scala @@ -0,0 +1,17 @@ +import scala.quoted.* + +def qwe(using Quotes) = { + import quotes.reflect.* + + def ko_1(param: ValDef | TypeDef) = + param match { + case _: ValDef => + case _: TypeDef => + } + + def ko_2(params: List[ValDef] | List[TypeDef]) = + params.map { + case x: ValDef => + case y: TypeDef => + } +} diff --git a/tests/pos-special/fatal-warnings/i12026.scala b/tests/pos-special/fatal-warnings/i12026.scala new file mode 100644 index 000000000000..4b6cf961b9c5 --- /dev/null +++ b/tests/pos-special/fatal-warnings/i12026.scala @@ -0,0 +1,5 @@ +def test[A, B](a: A|B)(using reflect.TypeTest[Any, A], reflect.TypeTest[Any, B]) = + a match { + case a: A => + case b: B => + } diff --git a/tests/pos-special/fatal-warnings/i12026b.scala b/tests/pos-special/fatal-warnings/i12026b.scala new file mode 100644 index 000000000000..9531247c2c1a --- /dev/null +++ b/tests/pos-special/fatal-warnings/i12026b.scala @@ -0,0 +1,5 @@ +def test[A, B](a: A|B)(tta: reflect.TypeTest[Any, A], ttb: reflect.TypeTest[Any, B]) = + a match { + case tta(a: A) => + case ttb(b: B) => + }