11package dotty .tools .dotc
22package transform
33
4- import core .Contexts ._
5- import core .Symbols ._
6- import core .Types ._
7- import core .Constants ._
8- import core .StdNames ._
9- import core .TypeErasure .isUnboundedGeneric
4+ import core ._
5+ import Contexts ._ , Symbols ._ , Types ._ , Constants ._ , StdNames ._ , Decorators ._
106import ast .Trees ._
117import Erasure .Boxing ._
12- import core . TypeErasure ._
8+ import TypeErasure ._
139import ValueClasses ._
10+ import core .Flags ._
11+ import util .Positions ._
12+
1413
1514/** This transform normalizes type tests and type casts,
1615 * also replacing type tests with singleton argument type with reference equality check
@@ -26,100 +25,140 @@ import ValueClasses._
2625trait TypeTestsCasts {
2726 import ast .tpd ._
2827
29- // override def phaseName: String = "typeTestsCasts"
30-
3128 def interceptTypeApply (tree : TypeApply )(implicit ctx : Context ): Tree = ctx.traceIndented(s " transforming ${tree.show}" , show = true ) {
3229 tree.fun match {
33- case fun @ Select (qual , selector) =>
30+ case fun @ Select (expr , selector) =>
3431 val sym = tree.symbol
3532
3633 def isPrimitive (tp : Type ) = tp.classSymbol.isPrimitiveValueClass
3734
38- def derivedTree (qual1 : Tree , sym : Symbol , tp : Type ) =
39- cpy.TypeApply (tree)(qual1.select(sym).withPos(qual.pos), List (TypeTree (tp)))
40-
41- def qualCls = qual.tpe.widen.classSymbol
42-
43- def transformIsInstanceOf (expr: Tree , argType : Type ): Tree = {
44- def argCls = argType.classSymbol
45- if ((expr.tpe <:< argType) && isPureExpr(expr))
46- Literal (Constant (true )) withPos tree.pos
47- else if (argCls.isPrimitiveValueClass)
48- if (qualCls.isPrimitiveValueClass) Literal (Constant (qualCls == argCls)) withPos tree.pos
49- else transformIsInstanceOf(expr, defn.boxedType(argCls.typeRef))
50- else argType.dealias match {
51- case _ : SingletonType =>
52- val cmpOp = if (argType derivesFrom defn.AnyValClass ) defn.Any_equals else defn.Object_eq
53- expr.select(cmpOp).appliedTo(singleton(argType))
54- case AndType (tp1, tp2) =>
55- evalOnce(expr) { fun =>
56- val erased1 = transformIsInstanceOf(fun, tp1)
57- val erased2 = transformIsInstanceOf(fun, tp2)
58- erased1 match {
59- case Literal (Constant (true )) => erased2
60- case _ =>
61- erased2 match {
62- case Literal (Constant (true )) => erased1
63- case _ => erased1 and erased2
64- }
65- }
35+ def derivedTree (expr1 : Tree , sym : Symbol , tp : Type ) =
36+ cpy.TypeApply (tree)(expr1.select(sym).withPos(expr.pos), List (TypeTree (tp)))
37+
38+ def foundCls = expr.tpe.widen.classSymbol
39+ // println(i"ta $tree, found = $foundCls")
40+
41+ def inMatch =
42+ fun.symbol == defn.Any_typeTest || // new scheme
43+ expr.symbol.is(Case ) // old scheme
44+
45+ def transformIsInstanceOf (expr: Tree , testType : Type , flagUnrelated : Boolean ): Tree = {
46+ def testCls = testType.classSymbol
47+
48+ def unreachable (why : => String ) =
49+ if (flagUnrelated)
50+ if (inMatch) ctx.error(em " this case is unreachable since $why" , expr.pos)
51+ else ctx.warning(em " this will always yield false since $why" , expr.pos)
52+
53+ /** Are `foundCls` and `testCls` classes that allow checks
54+ * whether a test would be always false?
55+ */
56+ def isCheckable =
57+ foundCls.isClass && testCls.isClass &&
58+ ! (testCls.isPrimitiveValueClass && ! foundCls.isPrimitiveValueClass) &&
59+ // if `test` is primitive but `found` is not, we might have a case like
60+ // found = java.lang.Integer, test = Int, which could be true
61+ // (not sure why that is so, but scalac behaves the same way)
62+ ! isDerivedValueClass(foundCls) && ! isDerivedValueClass(testCls)
63+ // we don't have the logic to handle derived value classes
64+
65+ /** Check whether a runtime test that a value of `foundCls` can be a `testCls`
66+ * can be true in some cases. Issure a warning or an error if that's not the case.
67+ */
68+ def checkSensical : Boolean =
69+ if (! isCheckable) true
70+ else if (foundCls.isPrimitiveValueClass && ! testCls.isPrimitiveValueClass) {
71+ ctx.error(" cannot test if value types are references" , tree.pos)
72+ false
6673 }
67- case defn.MultiArrayOf (elem, ndims) if isUnboundedGeneric(elem) =>
68- def isArrayTest (arg : Tree ) =
69- ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal (Constant (ndims)))
70- if (ndims == 1 ) isArrayTest(qual)
71- else evalOnce(qual) { qual1 =>
72- derivedTree(qual1, defn.Any_isInstanceOf , qual1.tpe) and isArrayTest(qual1)
74+ else if (! foundCls.derivesFrom(testCls)) {
75+ if (foundCls.is(Final )) {
76+ unreachable(i " $foundCls is not a subclass of $testCls" )
77+ false
7378 }
74- case _ =>
75- derivedTree(expr, defn.Any_isInstanceOf , argType)
76- }
79+ else if (! testCls.derivesFrom(foundCls) &&
80+ (testCls.is(Final ) ||
81+ ! testCls.is(Trait ) && ! foundCls.is(Trait ))) {
82+ unreachable(i " $foundCls and $testCls are unrelated " )
83+ false
84+ }
85+ else true
86+ }
87+ else true
88+
89+ if (expr.tpe <:< testType)
90+ if (expr.tpe.isNotNull) {
91+ ctx.warning(
92+ em " this will always yield true, since ` $foundCls` is a subclass of ` $testCls` " ,
93+ expr.pos)
94+ constant(expr, Literal (Constant (true )))
95+ }
96+ else expr.testNotNull
97+ else if (! checkSensical)
98+ constant(expr, Literal (Constant (false )))
99+ else if (testCls.isPrimitiveValueClass)
100+ if (foundCls.isPrimitiveValueClass)
101+ constant(expr, Literal (Constant (foundCls == testCls)))
102+ else
103+ transformIsInstanceOf(expr, defn.boxedType(testCls.typeRef), flagUnrelated)
104+ else
105+ derivedTree(expr, defn.Any_isInstanceOf , testType)
77106 }
78107
79- def transformAsInstanceOf (argType : Type ): Tree = {
80- def argCls = argType .widen.classSymbol
81- if (qual .tpe <:< argType )
82- Typed (qual , tree.args.head)
83- else if (qualCls .isPrimitiveValueClass) {
84- if (argCls .isPrimitiveValueClass) primitiveConversion(qual, argCls )
85- else derivedTree(box(qual ), defn.Any_asInstanceOf , argType )
108+ def transformAsInstanceOf (testType : Type ): Tree = {
109+ def testCls = testType .widen.classSymbol
110+ if (expr .tpe <:< testType )
111+ Typed (expr , tree.args.head)
112+ else if (foundCls .isPrimitiveValueClass) {
113+ if (testCls .isPrimitiveValueClass) primitiveConversion(expr, testCls )
114+ else derivedTree(box(expr ), defn.Any_asInstanceOf , testType )
86115 }
87- else if (argCls .isPrimitiveValueClass)
88- unbox(qual .ensureConforms(defn.ObjectType ), argType )
89- else if (isDerivedValueClass(argCls )) {
90- qual // adaptToType in Erasure will do the necessary type adaptation
116+ else if (testCls .isPrimitiveValueClass)
117+ unbox(expr .ensureConforms(defn.ObjectType ), testType )
118+ else if (isDerivedValueClass(testCls )) {
119+ expr // adaptToType in Erasure will do the necessary type adaptation
91120 }
92121 else
93- derivedTree(qual , defn.Any_asInstanceOf , argType )
122+ derivedTree(expr , defn.Any_asInstanceOf , testType )
94123 }
95124
96125 /** Transform isInstanceOf OrType
97126 *
98127 * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
99128 * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
100129 *
101- * The transform happens before erasure of `argType `, thus cannot be merged
102- * with `transformIsInstanceOf`, which depends on erased type of `argType `.
130+ * The transform happens before erasure of `testType `, thus cannot be merged
131+ * with `transformIsInstanceOf`, which depends on erased type of `testType `.
103132 */
104- def transformTypeTest (qual : Tree , argType : Type ): Tree = argType.dealias match {
133+ def transformTypeTest (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = testType.dealias match {
134+ case _ : SingletonType =>
135+ val cmpOp =
136+ if (testType derivesFrom defn.AnyValClass ) defn.Any_equals else defn.Object_eq
137+ expr.select(cmpOp).appliedTo(singleton(testType))
105138 case OrType (tp1, tp2) =>
106- evalOnce(qual) { fun =>
107- transformTypeTest(fun, tp1)
108- .select(defn.Boolean_|| )
109- .appliedTo(transformTypeTest(fun, tp2))
139+ evalOnce(expr) { e =>
140+ transformTypeTest(e, tp1, flagUnrelated = false )
141+ .or(transformTypeTest(e, tp2, flagUnrelated = false ))
110142 }
111143 case AndType (tp1, tp2) =>
112- evalOnce(qual) { fun =>
113- transformTypeTest(fun, tp1)
114- .select(defn.Boolean_&& )
115- .appliedTo(transformTypeTest(fun, tp2))
144+ evalOnce(expr) { e =>
145+ transformTypeTest(e, tp1, flagUnrelated)
146+ .and(transformTypeTest(e, tp2, flagUnrelated))
147+ }
148+ case defn.MultiArrayOf (elem, ndims) if isUnboundedGeneric(elem) =>
149+ def isArrayTest (arg : Tree ) =
150+ ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal (Constant (ndims)))
151+ if (ndims == 1 ) isArrayTest(expr)
152+ else evalOnce(expr) { e =>
153+ derivedTree(e, defn.Any_isInstanceOf , e.tpe)
154+ .and(isArrayTest(e))
116155 }
117156 case _ =>
118- transformIsInstanceOf(qual , erasure(argType) )
157+ transformIsInstanceOf(expr , erasure(testType), flagUnrelated )
119158 }
120159
121- if (sym eq defn.Any_isInstanceOf )
122- transformTypeTest(qual , tree.args.head.tpe)
160+ if (( sym eq defn.Any_isInstanceOf ) || (sym eq defn. Any_typeTest ) )
161+ transformTypeTest(expr , tree.args.head.tpe, flagUnrelated = true )
123162 else if (sym eq defn.Any_asInstanceOf )
124163 transformAsInstanceOf(erasure(tree.args.head.tpe))
125164 else tree
0 commit comments