Skip to content

Commit 6ff902b

Browse files
committed
Search for type tests when matching abstract applied type
Fixes #13433
1 parent c041327 commit 6ff902b

File tree

9 files changed

+150
-20
lines changed

9 files changed

+150
-20
lines changed

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
5050
(formal, span) => formal.argInfos match {
5151
case arg1 :: arg2 :: Nil if !defn.isBottomClass(arg2.typeSymbol) =>
5252
val tp1 = fullyDefinedType(arg1, "TypeTest argument", span)
53-
val tp2 = fullyDefinedType(arg2, "TypeTest argument", span)
53+
val tp2 = fullyDefinedType(arg2, "TypeTest argument", span).normalized
5454
val sym2 = tp2.typeSymbol
5555
if tp1 <:< tp2 then
5656
// optimization when we know the typetest will always succeed

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -901,28 +901,32 @@ class Typer extends Namer
901901
* exists, rewrite to `tt(e)`.
902902
* @pre We are in pattern-matching mode (Mode.Pattern)
903903
*/
904-
def tryWithTypeTest(tree: Typed, pt: Type)(using Context): Tree = tree.tpt.tpe.dealias match {
905-
case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper && !(tref =:= pt) =>
906-
def withTag(tpe: Type): Option[Tree] = {
907-
require(ctx.mode.is(Mode.Pattern))
908-
withoutMode(Mode.Pattern)(
909-
inferImplicit(tpe, EmptyTree, tree.tpt.span)
910-
) match
911-
case SearchSuccess(clsTag, _, _, _) =>
912-
withMode(Mode.InTypeTest) {
913-
Some(typed(untpd.Apply(untpd.TypedSplice(clsTag), untpd.TypedSplice(tree.expr)), pt))
914-
}
915-
case _ =>
916-
None
917-
}
918-
val tag = withTag(defn.TypeTestClass.typeRef.appliedTo(pt, tref))
919-
.orElse(withTag(defn.ClassTagClass.typeRef.appliedTo(tref)))
904+
def tryWithTypeTest(tree: Typed, pt: Type)(using Context): Tree =
905+
def withTag(tpe: Type): Option[Tree] = {
906+
require(ctx.mode.is(Mode.Pattern))
907+
withoutMode(Mode.Pattern)(
908+
inferImplicit(tpe, EmptyTree, tree.tpt.span)
909+
) match
910+
case SearchSuccess(clsTag, _, _, _) =>
911+
withMode(Mode.InTypeTest) {
912+
Some(typed(untpd.Apply(untpd.TypedSplice(clsTag), untpd.TypedSplice(tree.expr)), pt))
913+
}
914+
case _ =>
915+
None
916+
}
917+
def tagged(tpe: Type) = {
918+
val tag = withTag(defn.TypeTestClass.typeRef.appliedTo(pt, tpe))
919+
.orElse(withTag(defn.ClassTagClass.typeRef.appliedTo(tpe)))
920920
.getOrElse(tree)
921-
if tag.symbol.owner == defn.ClassTagClass && config.Feature.sourceVersion.isAtLeast(config.SourceVersion.future) then
921+
if tag.symbol.maybeOwner == defn.ClassTagClass && config.Feature.sourceVersion.isAtLeast(config.SourceVersion.future) then
922922
report.warning("Use of `scala.reflect.ClassTag` for type testing may be unsound. Consider using `scala.reflect.TypeTest` instead.", tree.srcPos)
923923
tag
924-
case _ => tree
925-
}
924+
}
925+
tree.tpt.tpe.dealias match {
926+
case tpe @ AppliedType(tref: TypeRef, _) if !tref.symbol.isClass && !ctx.isAfterTyper && !(tpe =:= pt) => tagged(tpe)
927+
case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper && !(tref =:= pt) => tagged(tref)
928+
case _ => tree
929+
}
926930

927931

928932
def typedNamedArg(tree: untpd.NamedArg, pt: Type)(using Context): NamedArg = {

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ i7212
99
i7868.scala
1010
i9011.scala
1111
i9473.scala
12+
i13433.scala
13+
i13433b.scala
1214
macros-in-same-project1
1315
mixin-forwarder-overload
1416
t10889
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import scala.reflect.TypeTest
2+
3+
type Matcher[A] = A match { case String => String }
4+
5+
def patternMatch[A](a: Any)(using tt: TypeTest[Any, Matcher[A]]): Option[Matcher[A]] = {
6+
// type T = RDF.Triple[Rdf]
7+
a match {
8+
case res: Matcher[A] => Some(res)
9+
case _ => None
10+
}
11+
}
12+
13+
def patternMatchWithAlias[A](a: Any)(using tt: TypeTest[Any, Matcher[A]]): Option[Matcher[A]] = {
14+
type T = Matcher[A]
15+
a match {
16+
case res: T => Some(res)
17+
case _ => None
18+
}
19+
}
20+
21+
22+
@main def main = {
23+
println(patternMatch[String]("abc"))
24+
println(patternMatchWithAlias[String]("abc"))
25+
println(patternMatch[String]("abc")(using (s: Any) => {
26+
if s.isInstanceOf[Matcher[String]] then Some[s.type & Matcher[String]](s.asInstanceOf[s.type & Matcher[String]]) else None }))
27+
println(patternMatchWithAlias[String]("abc")(using (s: Any) => {
28+
if s.isInstanceOf[Matcher[String]] then Some[s.type & Matcher[String]](s.asInstanceOf[s.type & Matcher[String]]) else None }))
29+
30+
println(patternMatch[String](1))
31+
println(patternMatchWithAlias[String](1))
32+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import scala.reflect.ClassTag
2+
3+
type Matcher[A] = A match { case String => String }
4+
5+
def patternMatch[A](a: Any)(using tt: ClassTag[Matcher[A]]): Option[Matcher[A]] = {
6+
// type T = RDF.Triple[Rdf]
7+
a match {
8+
case res: Matcher[A] => Some(res)
9+
case _ => None
10+
}
11+
}
12+
13+
def patternMatchWithAlias[A](a: Any)(using tt: ClassTag[Matcher[A]]): Option[Matcher[A]] = {
14+
type T = Matcher[A]
15+
a match {
16+
case res: T => Some(res)
17+
case _ => None
18+
}
19+
}
20+
21+
22+
@main def main = {
23+
println(patternMatch[String]("abc"))
24+
println(patternMatchWithAlias[String]("abc"))
25+
26+
println(patternMatch[String](1))
27+
println(patternMatchWithAlias[String](1))
28+
}

tests/run/i13433.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Some(abc)
2+
Some(abc)
3+
None
4+
None

tests/run/i13433.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import scala.reflect.TypeTest
2+
3+
type Matcher[A] = A match { case String => String }
4+
5+
def patternMatch[A](a: Any)(using tt: TypeTest[Any, Matcher[A]]): Option[Matcher[A]] = {
6+
// type T = RDF.Triple[Rdf]
7+
a match {
8+
case res: Matcher[A] => Some(res)
9+
case _ => None
10+
}
11+
}
12+
13+
def patternMatchWithAlias[A](a: Any)(using tt: TypeTest[Any, Matcher[A]]): Option[Matcher[A]] = {
14+
type T = Matcher[A]
15+
a match {
16+
case res: T => Some(res)
17+
case _ => None
18+
}
19+
}
20+
21+
22+
@main def Test = {
23+
println(patternMatch[String]("abc"))
24+
println(patternMatchWithAlias[String]("abc"))
25+
26+
println(patternMatch[String](1))
27+
println(patternMatchWithAlias[String](1))
28+
}

tests/run/i13433b.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Some(abc)
2+
Some(abc)
3+
None
4+
None

tests/run/i13433b.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import scala.reflect.ClassTag
2+
3+
type Matcher[A] = A match { case String => String }
4+
5+
def patternMatch[A](a: Any)(using tt: ClassTag[Matcher[A]]): Option[Matcher[A]] = {
6+
// type T = RDF.Triple[Rdf]
7+
a match {
8+
case res: Matcher[A] => Some(res)
9+
case _ => None
10+
}
11+
}
12+
13+
def patternMatchWithAlias[A](a: Any)(using tt: ClassTag[Matcher[A]]): Option[Matcher[A]] = {
14+
type T = Matcher[A]
15+
a match {
16+
case res: T => Some(res)
17+
case _ => None
18+
}
19+
}
20+
21+
22+
@main def Test = {
23+
println(patternMatch[String]("abc"))
24+
println(patternMatchWithAlias[String]("abc"))
25+
26+
println(patternMatch[String](1))
27+
println(patternMatchWithAlias[String](1))
28+
}

0 commit comments

Comments
 (0)