Skip to content

Commit d962d0b

Browse files
committed
Synthesize TypeTests
1 parent f04003c commit d962d0b

File tree

7 files changed

+117
-1
lines changed

7 files changed

+117
-1
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,8 @@ class Definitions {
632632
@tu lazy val ClassTagModule_apply: Symbol = ClassTagModule.requiredMethod(nme.apply)
633633

634634
@tu lazy val TypeTestClass: ClassSymbol = ctx.requiredClass("scala.reflect.TypeTest")
635+
@tu lazy val TypeTestModule: Symbol = TypeTestClass.companionModule
636+
@tu lazy val TypeTestModule_identity: Symbol = TypeTestModule.requiredMethod(nme.identity)
635637

636638
@tu lazy val QuotedExprClass: ClassSymbol = ctx.requiredClass("scala.quoted.Expr")
637639
@tu lazy val QuotedExprModule: Symbol = QuotedExprClass.companionModule

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,36 @@ trait Implicits { self: Typer =>
705705
EmptyTree
706706
}
707707

708+
lazy val synthesizedTypeTest: SpecialHandler =
709+
(formal, span) => implicit ctx => formal.argInfos match {
710+
case arg1 :: arg2 :: Nil if !defn.isBottomClass(arg2.typeSymbol) =>
711+
val tp1 = fullyDefinedType(arg1, "TypeTest argument", span)
712+
val tp2 = fullyDefinedType(arg2, "TypeTest argument", span)
713+
val sym2 = tp2.typeSymbol
714+
if tp1 <:< tp2 then
715+
ref(defn.TypeTestModule_identity).appliedToType(tp2).withSpan(span)
716+
else if sym2 == defn.AnyValClass || sym2 == defn.AnyRefAlias || sym2 == defn.ObjectClass then
717+
EmptyTree
718+
else
719+
// Generate SAM: (s: <tp1>) => if s.isInstanceOf[s.type & <tp2>] then Some(s.asInstanceOf[s.type & <tp2>]) else None
720+
def body(args: List[Tree]): Tree = {
721+
val arg :: Nil = args
722+
val t = arg.tpe & tp2
723+
If(
724+
arg.select(defn.Any_isInstanceOf).appliedToType(t),
725+
ref(defn.SomeClass.companionModule.termRef).select(nme.apply)
726+
.appliedToType(t)
727+
.appliedTo(arg.select(nme.asInstanceOf_).appliedToType(t)),
728+
ref(defn.NoneModule))
729+
}
730+
val tpe = MethodType(List(nme.s))(_ => List(tp1), mth => defn.OptionClass.typeRef.appliedTo(mth.newParamRef(0) & tp2))
731+
val meth = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, tpe, coord = span)
732+
val typeTestType = defn.TypeTestClass.typeRef.appliedTo(List(tp1, tp2))
733+
Closure(meth, tss => body(tss.head).changeOwner(ctx.owner, meth), targetType = typeTestType).withSpan(span)
734+
case _ =>
735+
EmptyTree
736+
}
737+
708738
/** Synthesize the tree for `'[T]` for an implicit `scala.quoted.Type[T]`.
709739
* `T` is deeply dealiased to avoid references to local type aliases.
710740
*/
@@ -1076,6 +1106,7 @@ trait Implicits { self: Typer =>
10761106
if (mySpecialHandlers == null)
10771107
mySpecialHandlers = List(
10781108
defn.ClassTagClass -> synthesizedClassTag,
1109+
defn.TypeTestClass -> synthesizedTypeTest,
10791110
defn.QuotedTypeClass -> synthesizedTypeTag,
10801111
defn.QuoteContextClass -> synthesizedQuoteContext,
10811112
defn.EqlClass -> synthesizedEq,

library/src/scala/reflect/TypeTest.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@ trait TypeTest[-S, T] extends Serializable {
2525
def unapply(x: S): Option[x.type & T]
2626

2727
}
28+
29+
object TypeTest {
30+
31+
def identity[T]: TypeTest[T, T] = Some(_)
32+
33+
}

tests/neg-custom-args/fatal-warnings/IsInstanceOfClassTag2.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ object IsInstanceOfClassTag {
1414
case Some(xs) =>
1515
}
1616

17-
safeCast[List[_]](List[Int](1)) match { // error
17+
safeCast[List[_]](List[Int](1)) match {
1818
case None =>
1919
case Some(xs) =>
2020
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import scala.reflect.TypeTest
2+
3+
object Test {
4+
def test[S, T](given TypeTest[S, T]): Unit = ()
5+
val a: A = ???
6+
7+
test[Any, Any]
8+
test[Int, Int]
9+
10+
test[Int, Any]
11+
test[String, Any]
12+
test[String, AnyRef]
13+
14+
test[Any, Int]
15+
test[Any, String]
16+
test[Any, Some[_]]
17+
test[Any, Array[Int]]
18+
test[Seq[Int], List[Int]]
19+
20+
test[Any, Some[Int]] // error
21+
test[Any, a.X] // error
22+
test[a.X, a.Y] // error
23+
24+
}
25+
26+
class A {
27+
type X
28+
type Y <: X
29+
}

tests/neg/type-test-syntesize.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import scala.reflect.TypeTest
2+
3+
object Test {
4+
def test[S, T](given x: TypeTest[S, T]): Unit = ()
5+
6+
test[Any, AnyRef] // error
7+
test[Any, AnyVal] // error
8+
test[Any, Object] // error
9+
10+
test[Any, Nothing] // error
11+
test[AnyRef, Nothing] // error
12+
test[AnyVal, Nothing] // error
13+
test[Null, Nothing] // error
14+
test[Unit, Nothing] // error
15+
test[Int, Nothing] // error
16+
test[8, Nothing] // error
17+
test[List[_], Nothing] // error
18+
test[Nothing, Nothing] // error
19+
}

tests/pos/type-test-syntesize.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import scala.reflect.TypeTest
2+
3+
object Test {
4+
def test[S, T](given TypeTest[S, T]): Unit = ()
5+
val a: A = ???
6+
7+
test[Any, Any]
8+
test[Int, Int]
9+
10+
test[Int, Any]
11+
test[String, Any]
12+
test[String, AnyRef]
13+
14+
test[Any, Int]
15+
test[Any, String]
16+
test[Any, Some[_]]
17+
test[Any, Array[Int]]
18+
test[Seq[Int], List[Int]]
19+
20+
test[Any, Some[Int]] // unchecked warning
21+
test[Any, a.X] // unchecked warning
22+
test[a.X, a.Y] // unchecked warning
23+
24+
}
25+
26+
class A {
27+
type X
28+
type Y <: X
29+
}

0 commit comments

Comments
 (0)