Skip to content

Commit 94d8313

Browse files
committed
handle Array in isInstanceOf check
1 parent b96b5cc commit 94d8313

File tree

3 files changed

+99
-23
lines changed

3 files changed

+99
-23
lines changed

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object Printers {
3131
val plugins: Printer = noPrinter
3232
val simplify: Printer = noPrinter
3333
val subtyping: Printer = noPrinter
34-
val transforms: Printer = noPrinter
34+
val transforms: Printer = new Printer
3535
val typr: Printer = noPrinter
3636
val unapp: Printer = noPrinter
3737
val variances: Printer = noPrinter

compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Contexts.Context, Types._, Decorators._, Symbols._, typer._
88
import TypeUtils._, Flags._
99
import config.Printers.{ transforms => debug }
1010

11-
/** check runtime realizability of type test
11+
/** Check runtime realizability of type test, see the documentation for `Checkable`.
1212
*/
1313
class IsInstanceOfChecker extends MiniPhase {
1414

@@ -20,7 +20,7 @@ class IsInstanceOfChecker extends MiniPhase {
2020
def ensureCheckable(qual: Tree, pt: Tree): Tree = {
2121
if (!Checkable.checkable(qual.tpe, pt.tpe))
2222
ctx.warning(
23-
s"the type test for ${pt.show} cannot be checked at runtime",
23+
s"the type test for ${pt} cannot be checked at runtime",
2424
tree.pos
2525
)
2626

@@ -43,42 +43,60 @@ object Checkable {
4343

4444
/** Whether `(x:X).isInstanceOf[P]` can be checked at runtime?
4545
*
46-
* The following cases are not checkable at runtime:
47-
*
48-
* 1. if `P` refers to an abstract type member
49-
* 2. if `P` is `pre.F[Ts]` and `pre.F` refers to a class:
46+
* 0. if `P` is a singleton type, TRUE
47+
* 1. if `P` refers to an abstract type member, FALSE
48+
* 2. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`.
49+
* 3. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`:
5050
* (a) replace `Ts` with fresh type variables `Xs`
5151
* (b) instantiate `Xs` with the constraint `pre.F[Xs] <:< X`
52-
* (c) `pre.F[Xs] <:< P` doesn't hold
53-
* 3. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
52+
* (c) `pre.F[Xs] <:< P2`, where `P2` is `P` with pattern binder types (e.g., `_$1`)
53+
* replaced with `WildcardType`.
54+
* 4. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
55+
* 5. otherwise, TRUE
5456
*/
5557
def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = {
5658
def Psym = P.dealias.typeSymbol
5759

5860
def isAbstract = !Psym.isClass
5961

60-
def isClassDetermined(tpe: AppliedType) = {
62+
def replaceBinderMap(implicit ctx: Context) = new TypeMap {
63+
def apply(tp: Type) = tp match {
64+
case tref: TypeRef if !tref.typeSymbol.isClass && tref.symbol.is(Case) => WildcardType
65+
case _ => mapOver(tp)
66+
}
67+
}
68+
69+
def isClassDetermined(tpe: AppliedType)(implicit ctx: Context) = {
6170
val AppliedType(tycon, args) = tpe
6271
val tvars = tycon.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) }
63-
val P2 = tycon.appliedTo(tvars)
72+
val P1 = tycon.appliedTo(tvars)
6473

65-
debug.println("P2 : " + P2)
66-
debug.println("X : " + X)
74+
debug.println("P1 : " + P1)
75+
debug.println("X : " + X.widen)
6776

68-
!(P2 <:< X.widen) || {
69-
val syms = maximizeType(P2, Psym.pos, fromScala2x = false)
70-
val res = P2 <:< P
71-
debug.println("P2: " + P2.show)
72-
debug.println("P2 <:< P = " + res)
77+
!(P1 <:< X.widen) || {
78+
// val syms = maximizeType(P1, Psym.pos, fromScala2x = false)
79+
isFullyDefined(P1, ForceDegree.noBottom)
80+
val P2 = replaceBinderMap.apply(P)
81+
val res = P1 <:< P2
82+
debug.println("P1: " + P1)
83+
debug.println("P2: " + P2)
84+
debug.println("P1 <:< P2 = " + res)
7385
res
7486
}
7587
}
7688

7789
P match {
78-
case tpe: AppliedType => !isAbstract && isClassDetermined(tpe)
79-
case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2)
80-
case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2)
81-
case _ => !isAbstract
90+
case _: SingletonType => true
91+
case defn.ArrayOf(tpT) =>
92+
X match {
93+
case defn.ArrayOf(tpE) => checkable(tpE, tpT)
94+
case _ => checkable(defn.AnyType, tpT)
95+
}
96+
case tpe: AppliedType => !isAbstract && isClassDetermined(tpe)(ctx.fresh.setFreshGADTBounds)
97+
case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2)
98+
case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2)
99+
case _ => !isAbstract
82100
}
83101
}
84-
}
102+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// Test cases: the only place we can cut and paste without crying
2+
// ourself to sleep.
3+
object Test {
4+
def f1(a: Any) = a match {
5+
case x: Array[Int] => x(0)
6+
case x: Array[Double] => 2
7+
case x: Array[Float] => x.sum.toInt
8+
case x: Array[String] => x.size
9+
case x: Array[AnyRef] => 5
10+
case x: Array[_] => 6
11+
case _ => 7
12+
}
13+
def f2(a: Array[_]) = a match {
14+
case x: Array[Int] => x(0)
15+
case x: Array[Double] => 2
16+
case x: Array[Float] => x.sum.toInt
17+
case x: Array[String] => x.size
18+
case x: Array[AnyRef] => 5
19+
case x: Array[_] => 6
20+
case _ => 7
21+
}
22+
def f3[T](a: Array[T]) = a match {
23+
case x: Array[Int] => x(0)
24+
case x: Array[Double] => 2
25+
case x: Array[Float] => x.sum.toInt
26+
case x: Array[String] => x.size
27+
case x: Array[AnyRef] => 5
28+
case x: Array[_] => 6
29+
case _ => 7
30+
}
31+
32+
33+
def main(args: Array[String]): Unit = {
34+
println(f1(Array(1, 2, 3)))
35+
println(f1(Array(1.0, -2.0, 3.0, 1.0)))
36+
println(f1(Array(1.0f, 2.0f, 3.0f, -3.0f)))
37+
println(f1((1 to 4).toArray map (_.toString)))
38+
println(f1(new Array[Any](10))) // should match as Array[AnyRef]
39+
println(f1(Array(1L)))
40+
println(f1(null))
41+
42+
println(f2(Array(1, 2, 3)))
43+
println(f2(Array(1.0, -2.0, 3.0, 1.0)))
44+
println(f2(Array(1.0f, 2.0f, 3.0f, -3.0f)))
45+
println(f2((1 to 4).toArray map (_.toString)))
46+
println(f2(new Array[Any](10))) // should match as Array[AnyRef]
47+
println(f2(Array(1L)))
48+
println(f2(null))
49+
50+
println(f3(Array(1, 2, 3)))
51+
println(f3(Array(1.0, -2.0, 3.0, 1.0)))
52+
println(f3(Array(1.0f, 2.0f, 3.0f, -3.0f)))
53+
println(f3((1 to 4).toArray map (_.toString)))
54+
println(f3(new Array[Any](10))) // should match as Array[AnyRef]
55+
println(f3(Array(1L)))
56+
println(f3(null))
57+
}
58+
}

0 commit comments

Comments
 (0)