1
1
package dotty .tools .dotc
2
2
package transform
3
3
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 ._
10
6
import ast .Trees ._
11
7
import Erasure .Boxing ._
12
- import core . TypeErasure ._
8
+ import TypeErasure ._
13
9
import ValueClasses ._
10
+ import core .Flags ._
11
+ import util .Positions ._
12
+
14
13
15
14
/** This transform normalizes type tests and type casts,
16
15
* also replacing type tests with singleton argument type with reference equality check
@@ -26,100 +25,140 @@ import ValueClasses._
26
25
trait TypeTestsCasts {
27
26
import ast .tpd ._
28
27
29
- // override def phaseName: String = "typeTestsCasts"
30
-
31
28
def interceptTypeApply (tree : TypeApply )(implicit ctx : Context ): Tree = ctx.traceIndented(s " transforming ${tree.show}" , show = true ) {
32
29
tree.fun match {
33
- case fun @ Select (qual , selector) =>
30
+ case fun @ Select (expr , selector) =>
34
31
val sym = tree.symbol
35
32
36
33
def isPrimitive (tp : Type ) = tp.classSymbol.isPrimitiveValueClass
37
34
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
66
73
}
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
73
78
}
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)
77
106
}
78
107
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 )
86
115
}
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
91
120
}
92
121
else
93
- derivedTree(qual , defn.Any_asInstanceOf , argType )
122
+ derivedTree(expr , defn.Any_asInstanceOf , testType )
94
123
}
95
124
96
125
/** Transform isInstanceOf OrType
97
126
*
98
127
* expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
99
128
* expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
100
129
*
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 `.
103
132
*/
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))
105
138
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 ))
110
142
}
111
143
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))
116
155
}
117
156
case _ =>
118
- transformIsInstanceOf(qual , erasure(argType) )
157
+ transformIsInstanceOf(expr , erasure(testType), flagUnrelated )
119
158
}
120
159
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 )
123
162
else if (sym eq defn.Any_asInstanceOf )
124
163
transformAsInstanceOf(erasure(tree.args.head.tpe))
125
164
else tree
0 commit comments