Skip to content

Commit 4987f48

Browse files
committed
Search for type tests when matching abstract applied type
Fixes #13433
1 parent c574f4f commit 4987f48

File tree

9 files changed

+146
-20
lines changed

9 files changed

+146
-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: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -888,28 +888,32 @@ class Typer extends Namer
888888
* exists, rewrite to `tt(e)`.
889889
* @pre We are in pattern-matching mode (Mode.Pattern)
890890
*/
891-
def tryWithTypeTest(tree: Typed, pt: Type)(using Context): Tree = tree.tpt.tpe.dealias match {
892-
case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper && !(tref =:= pt) =>
893-
def withTag(tpe: Type): Option[Tree] = {
894-
require(ctx.mode.is(Mode.Pattern))
895-
withoutMode(Mode.Pattern)(
896-
inferImplicit(tpe, EmptyTree, tree.tpt.span)
897-
) match
898-
case SearchSuccess(clsTag, _, _, _) =>
899-
withMode(Mode.InTypeTest) {
900-
Some(typed(untpd.Apply(untpd.TypedSplice(clsTag), untpd.TypedSplice(tree.expr)), pt))
901-
}
902-
case _ =>
903-
None
904-
}
905-
val tag = withTag(defn.TypeTestClass.typeRef.appliedTo(pt, tref))
906-
.orElse(withTag(defn.ClassTagClass.typeRef.appliedTo(tref)))
891+
def tryWithTypeTest(tree: Typed, pt: Type)(using Context): Tree =
892+
def withTag(tpe: Type): Option[Tree] = {
893+
require(ctx.mode.is(Mode.Pattern))
894+
withoutMode(Mode.Pattern)(
895+
inferImplicit(tpe, EmptyTree, tree.tpt.span)
896+
) match
897+
case SearchSuccess(clsTag, _, _, _) =>
898+
withMode(Mode.InTypeTest) {
899+
Some(typed(untpd.Apply(untpd.TypedSplice(clsTag), untpd.TypedSplice(tree.expr)), pt))
900+
}
901+
case _ =>
902+
None
903+
}
904+
def tagged(tpe: Type) = {
905+
val tag = withTag(defn.TypeTestClass.typeRef.appliedTo(pt, tpe))
906+
.orElse(withTag(defn.ClassTagClass.typeRef.appliedTo(tpe)))
907907
.getOrElse(tree)
908908
if tag.symbol.owner == defn.ClassTagClass && config.Feature.sourceVersion.isAtLeast(config.SourceVersion.future) then
909909
report.warning("Use of `scala.reflect.ClassTag` for type testing may be unsound. Consider using `scala.reflect.TypeTest` instead.", tree.srcPos)
910910
tag
911-
case _ => tree
912-
}
911+
}
912+
tree.tpt.tpe.dealias match {
913+
case tpe @ AppliedType(tref: TypeRef, _) if !tref.symbol.isClass && !ctx.isAfterTyper && !(tpe =:= pt) => tagged(tpe)
914+
case tref: TypeRef if !tref.symbol.isClass && !ctx.isAfterTyper && !(tref =:= pt) => tagged(tref)
915+
case _ => tree
916+
}
913917

914918

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

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ i7212
66
i7868.scala
77
i9011.scala
88
i9473.scala
9+
i13433.scala
10+
i13433b.scala
911
macros-in-same-project1
1012
mixin-forwarder-overload
1113
t10889
@@ -36,4 +38,4 @@ zero-arity-case-class.scala
3638
i12194.scala
3739
i12753
3840
t6138
39-
t6138-2
41+
t6138-2
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 main = {
23+
println(patternMatch[String]("abc"))
24+
println(patternMatchWithAlias[String]("abc"))
25+
26+
println(patternMatch[String](1))
27+
println(patternMatchWithAlias[String](1))
28+
}
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)