@@ -27,20 +27,20 @@ trait TypeTestsCasts {
27
27
28
28
def interceptTypeApply (tree : TypeApply )(implicit ctx : Context ): Tree = ctx.traceIndented(s " transforming ${tree.show}" , show = true ) {
29
29
tree.fun match {
30
- case fun @ Select (qual , selector) =>
30
+ case fun @ Select (expr , selector) =>
31
31
val sym = tree.symbol
32
32
33
33
def isPrimitive (tp : Type ) = tp.classSymbol.isPrimitiveValueClass
34
34
35
- def derivedTree (qual1 : Tree , sym : Symbol , tp : Type ) =
36
- cpy.TypeApply (tree)(qual1 .select(sym).withPos(qual .pos), List (TypeTree (tp)))
35
+ def derivedTree (expr1 : Tree , sym : Symbol , tp : Type ) =
36
+ cpy.TypeApply (tree)(expr1 .select(sym).withPos(expr .pos), List (TypeTree (tp)))
37
37
38
- def foundCls = qual .tpe.widen.classSymbol
38
+ def foundCls = expr .tpe.widen.classSymbol
39
39
// println(i"ta $tree, found = $foundCls")
40
40
41
41
def inMatch =
42
42
fun.symbol == defn.Any_typeTest || // new scheme
43
- qual .symbol.is(Case ) // old scheme
43
+ expr .symbol.is(Case ) // old scheme
44
44
45
45
def transformIsInstanceOf (expr: Tree , testType : Type , flagUnrelated : Boolean ): Tree = {
46
46
def testCls = testType.classSymbol
@@ -50,28 +50,40 @@ trait TypeTestsCasts {
50
50
if (inMatch) ctx.error(em " this case is unreachable since $why" , expr.pos)
51
51
else ctx.warning(em " this will always yield false since $why" , expr.pos)
52
52
53
- def checkSensical : Boolean = {
54
- val foundCls = erasure(qual.tpe.widen).typeSymbol
55
- val testCls = testType.typeSymbol
56
- ! foundCls.isClass ||
57
- ! testCls.isClass ||
58
- foundCls.isPrimitiveValueClass != testCls.isPrimitiveValueClass ||
59
- ValueClasses .isDerivedValueClass(foundCls) ||
60
- ValueClasses .isDerivedValueClass(testCls) ||
61
- foundCls == defn.ObjectClass ||
62
- foundCls.derivesFrom(testCls) || {
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
+ foundCls.isPrimitiveValueClass == testCls.isPrimitiveValueClass &&
59
+ // if `found` is primitive but `test` is not, it's illegal anyway
60
+ // if `test` is primitive but `found` is not, we might have a case like
61
+ // found = java.lang.Integer, test = Int, which could be true
62
+ // (not sure why that is so, but scalac behaves the same way)
63
+ ! isDerivedValueClass(foundCls) && ! isDerivedValueClass(testCls) &&
64
+ // we don't have the logic to handle derived value classes
65
+ foundCls != defn.ObjectClass
66
+ // if `foundCls == Object`, it could have been `Any` before erasure.
67
+
68
+ /** Check whether a runtime test that a value of `foundCls` can be a `testCls`
69
+ * can be true in some cases. Issure a warning or an error if that's not the case.
70
+ */
71
+ def checkSensical : Boolean =
72
+ if (! isCheckable) true
73
+ else if (! foundCls.derivesFrom(testCls)) {
63
74
if (foundCls.is(Final )) {
64
75
unreachable(i " $foundCls is not a subclass of $testCls" )
65
76
false
66
77
}
67
78
else if (! testCls.derivesFrom(foundCls) &&
68
- (testCls.is(Final ) || ! testCls.is(Trait ) && ! foundCls.is(Trait ))) {
79
+ (testCls.is(Final ) ||
80
+ ! testCls.is(Trait ) && ! foundCls.is(Trait ))) {
69
81
unreachable(i " $foundCls and $testCls are unrelated " )
70
82
false
71
83
}
72
84
else true
73
85
}
74
- }
86
+ else true
75
87
76
88
if (expr.tpe <:< testType)
77
89
if (expr.tpe.isNotNull) {
@@ -88,32 +100,25 @@ trait TypeTestsCasts {
88
100
constant(expr, Literal (Constant (foundCls == testCls)))
89
101
else
90
102
transformIsInstanceOf(expr, defn.boxedType(testCls.typeRef), flagUnrelated)
91
- else testType.dealias match {
92
- case _ : SingletonType =>
93
- val cmpOp =
94
- if (testType derivesFrom defn.AnyValClass ) defn.Any_equals
95
- else defn.Object_eq
96
- expr.select(cmpOp).appliedTo(singleton(testType))
97
- case _ =>
98
- derivedTree(expr, defn.Any_isInstanceOf , testType)
99
- }
103
+ else
104
+ derivedTree(expr, defn.Any_isInstanceOf , testType)
100
105
}
101
106
102
107
def transformAsInstanceOf (testType : Type ): Tree = {
103
108
def testCls = testType.widen.classSymbol
104
- if (qual .tpe <:< testType)
105
- Typed (qual , tree.args.head)
109
+ if (expr .tpe <:< testType)
110
+ Typed (expr , tree.args.head)
106
111
else if (foundCls.isPrimitiveValueClass) {
107
- if (testCls.isPrimitiveValueClass) primitiveConversion(qual , testCls)
108
- else derivedTree(box(qual ), defn.Any_asInstanceOf , testType)
112
+ if (testCls.isPrimitiveValueClass) primitiveConversion(expr , testCls)
113
+ else derivedTree(box(expr ), defn.Any_asInstanceOf , testType)
109
114
}
110
115
else if (testCls.isPrimitiveValueClass)
111
- unbox(qual .ensureConforms(defn.ObjectType ), testType)
116
+ unbox(expr .ensureConforms(defn.ObjectType ), testType)
112
117
else if (isDerivedValueClass(testCls)) {
113
- qual // adaptToType in Erasure will do the necessary type adaptation
118
+ expr // adaptToType in Erasure will do the necessary type adaptation
114
119
}
115
120
else
116
- derivedTree(qual , defn.Any_asInstanceOf , testType)
121
+ derivedTree(expr , defn.Any_asInstanceOf , testType)
117
122
}
118
123
119
124
/** Transform isInstanceOf OrType
@@ -124,32 +129,35 @@ trait TypeTestsCasts {
124
129
* The transform happens before erasure of `testType`, thus cannot be merged
125
130
* with `transformIsInstanceOf`, which depends on erased type of `testType`.
126
131
*/
127
- def transformTypeTest (qual : Tree , testType : Type , flagUnrelated : Boolean ): Tree = testType.dealias match {
132
+ def transformTypeTest (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = testType.dealias match {
133
+ case _ : SingletonType =>
134
+ val cmpOp =
135
+ if (testType derivesFrom defn.AnyValClass ) defn.Any_equals else defn.Object_eq
136
+ expr.select(cmpOp).appliedTo(singleton(testType))
128
137
case OrType (tp1, tp2) =>
129
- evalOnce(qual) { fun =>
130
- transformTypeTest(fun, tp1, flagUnrelated = false )
131
- .select(defn.Boolean_|| )
132
- .appliedTo(transformTypeTest(fun, tp2, flagUnrelated = false ))
138
+ evalOnce(expr) { e =>
139
+ transformTypeTest(e, tp1, flagUnrelated = false )
140
+ .or(transformTypeTest(e, tp2, flagUnrelated = false ))
133
141
}
134
142
case AndType (tp1, tp2) =>
135
- evalOnce(qual) { fun =>
136
- transformTypeTest(fun, tp1, flagUnrelated)
137
- .select(defn.Boolean_&& )
138
- .appliedTo(transformTypeTest(fun, tp2, flagUnrelated))
143
+ evalOnce(expr) { e =>
144
+ transformTypeTest(e, tp1, flagUnrelated)
145
+ .and(transformTypeTest(e, tp2, flagUnrelated))
139
146
}
140
147
case defn.MultiArrayOf (elem, ndims) if isUnboundedGeneric(elem) =>
141
148
def isArrayTest (arg : Tree ) =
142
149
ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal (Constant (ndims)))
143
- if (ndims == 1 ) isArrayTest(qual)
144
- else evalOnce(qual) { qual1 =>
145
- derivedTree(qual1, defn.Any_isInstanceOf , qual1.tpe) and isArrayTest(qual1)
150
+ if (ndims == 1 ) isArrayTest(expr)
151
+ else evalOnce(expr) { e =>
152
+ derivedTree(e, defn.Any_isInstanceOf , e.tpe)
153
+ .and(isArrayTest(e))
146
154
}
147
155
case _ =>
148
- transformIsInstanceOf(qual , erasure(testType), flagUnrelated)
156
+ transformIsInstanceOf(expr , erasure(testType), flagUnrelated)
149
157
}
150
158
151
159
if ((sym eq defn.Any_isInstanceOf ) || (sym eq defn.Any_typeTest ))
152
- transformTypeTest(qual , tree.args.head.tpe, flagUnrelated = true )
160
+ transformTypeTest(expr , tree.args.head.tpe, flagUnrelated = true )
153
161
else if (sym eq defn.Any_asInstanceOf )
154
162
transformAsInstanceOf(erasure(tree.args.head.tpe))
155
163
else tree
0 commit comments