@@ -156,159 +156,161 @@ object TypeTestsCasts {
156
156
}
157
157
158
158
def interceptTypeApply (tree : TypeApply )(implicit ctx : Context ): Tree = trace(s " transforming ${tree.show}" , show = true ) {
159
- tree.fun match {
160
- case fun @ Select (expr, selector) =>
161
- val sym = tree.symbol
159
+ val expr = tree.fun match {
160
+ case Select (expr, _) => expr
161
+ case i : Ident => desugarIdentPrefix(i)
162
+ case _ => EmptyTree
163
+ }
164
+ if (expr.isEmpty) tree
165
+ else {
166
+ val sym = tree.symbol
167
+
168
+ def isPrimitive (tp : Type ) = tp.classSymbol.isPrimitiveValueClass
162
169
163
- def isPrimitive (tp : Type ) = tp.classSymbol.isPrimitiveValueClass
170
+ def derivedTree (expr1 : Tree , sym : Symbol , tp : Type ) =
171
+ cpy.TypeApply (tree)(expr1.select(sym).withSpan(expr.span), List (TypeTree (tp)))
164
172
165
- def derivedTree (expr1 : Tree , sym : Symbol , tp : Type ) =
166
- cpy.TypeApply (tree)(expr1.select(sym).withSpan(expr.span), List (TypeTree (tp)))
173
+ def effectiveClass (tp : Type ): Symbol =
174
+ if (tp.isRef(defn.PairClass )) effectiveClass(erasure(tp))
175
+ else tp.classSymbol
167
176
168
- def effectiveClass (tp : Type ): Symbol =
169
- if (tp.isRef(defn.PairClass )) effectiveClass(erasure(tp))
170
- else tp.classSymbol
177
+ def foundCls = effectiveClass(expr.tpe.widen)
171
178
172
- def foundCls = effectiveClass(expr.tpe.widen)
179
+ def inMatch =
180
+ tree.fun.symbol == defn.Any_typeTest || // new scheme
181
+ expr.symbol.is(Case ) // old scheme
173
182
174
- def inMatch =
175
- fun.symbol == defn.Any_typeTest || // new scheme
176
- expr.symbol.is(Case ) // old scheme
183
+ def transformIsInstanceOf (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = {
184
+ def testCls = effectiveClass(testType.widen)
177
185
178
- def transformIsInstanceOf (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = {
179
- def testCls = effectiveClass(testType.widen)
186
+ def unreachable (why : => String ): Boolean = {
187
+ if (flagUnrelated)
188
+ if (inMatch) ctx.error(em " this case is unreachable since $why" , expr.sourcePos)
189
+ else ctx.warning(em " this will always yield false since $why" , expr.sourcePos)
190
+ false
191
+ }
180
192
181
- def unreachable (why : => String ): Boolean = {
182
- if (flagUnrelated)
183
- if (inMatch) ctx.error(em " this case is unreachable since $why" , expr.sourcePos)
184
- else ctx.warning(em " this will always yield false since $why" , expr.sourcePos)
193
+ /** Are `foundCls` and `testCls` classes that allow checks
194
+ * whether a test would be always false?
195
+ */
196
+ def isCheckable =
197
+ foundCls.isClass && testCls.isClass &&
198
+ ! (testCls.isPrimitiveValueClass && ! foundCls.isPrimitiveValueClass) &&
199
+ // if `test` is primitive but `found` is not, we might have a case like
200
+ // found = java.lang.Integer, test = Int, which could be true
201
+ // (not sure why that is so, but scalac behaves the same way)
202
+ ! isDerivedValueClass(foundCls) && ! isDerivedValueClass(testCls)
203
+ // we don't have the logic to handle derived value classes
204
+
205
+ /** Check whether a runtime test that a value of `foundCls` can be a `testCls`
206
+ * can be true in some cases. Issues a warning or an error otherwise.
207
+ */
208
+ def checkSensical : Boolean =
209
+ if (! isCheckable) true
210
+ else if (foundCls.isPrimitiveValueClass && ! testCls.isPrimitiveValueClass) {
211
+ ctx.error(" cannot test if value types are references" , tree.sourcePos)
185
212
false
186
213
}
187
-
188
- /** Are `foundCls` and `testCls` classes that allow checks
189
- * whether a test would be always false?
190
- */
191
- def isCheckable =
192
- foundCls.isClass && testCls.isClass &&
193
- ! (testCls.isPrimitiveValueClass && ! foundCls.isPrimitiveValueClass) &&
194
- // if `test` is primitive but `found` is not, we might have a case like
195
- // found = java.lang.Integer, test = Int, which could be true
196
- // (not sure why that is so, but scalac behaves the same way)
197
- ! isDerivedValueClass(foundCls) && ! isDerivedValueClass(testCls)
198
- // we don't have the logic to handle derived value classes
199
-
200
- /** Check whether a runtime test that a value of `foundCls` can be a `testCls`
201
- * can be true in some cases. Issues a warning or an error otherwise.
202
- */
203
- def checkSensical : Boolean =
204
- if (! isCheckable) true
205
- else if (foundCls.isPrimitiveValueClass && ! testCls.isPrimitiveValueClass) {
206
- ctx.error(" cannot test if value types are references" , tree.sourcePos)
207
- false
208
- }
209
- else if (! foundCls.derivesFrom(testCls)) {
210
- val unrelated = ! testCls.derivesFrom(foundCls) && (
211
- testCls.is(Final ) || ! testCls.is(Trait ) && ! foundCls.is(Trait )
212
- )
213
- if (foundCls.is(Final ))
214
- unreachable(i " $foundCls is not a subclass of $testCls" )
215
- else if (unrelated)
216
- unreachable(i " $foundCls and $testCls are unrelated " )
217
- else true
218
- }
214
+ else if (! foundCls.derivesFrom(testCls)) {
215
+ val unrelated = ! testCls.derivesFrom(foundCls) && (
216
+ testCls.is(Final ) || ! testCls.is(Trait ) && ! foundCls.is(Trait )
217
+ )
218
+ if (foundCls.is(Final ))
219
+ unreachable(i " $foundCls is not a subclass of $testCls" )
220
+ else if (unrelated)
221
+ unreachable(i " $foundCls and $testCls are unrelated " )
219
222
else true
223
+ }
224
+ else true
220
225
221
- if (expr.tpe <:< testType)
222
- if (expr.tpe.isNotNull) {
223
- if (! inMatch) ctx.warning(TypeTestAlwaysSucceeds (foundCls, testCls), tree.sourcePos)
224
- constant(expr, Literal (Constant (true )))
225
- }
226
- else expr.testNotNull
227
- else if (! checkSensical)
228
- constant(expr, Literal (Constant (false )))
229
- else if (testCls.isPrimitiveValueClass)
230
- if (foundCls.isPrimitiveValueClass)
231
- constant(expr, Literal (Constant (foundCls == testCls)))
232
- else
233
- transformIsInstanceOf(expr, defn.boxedType(testCls.typeRef), flagUnrelated)
226
+ if (expr.tpe <:< testType)
227
+ if (expr.tpe.isNotNull) {
228
+ if (! inMatch) ctx.warning(TypeTestAlwaysSucceeds (foundCls, testCls), tree.sourcePos)
229
+ constant(expr, Literal (Constant (true )))
230
+ }
231
+ else expr.testNotNull
232
+ else if (! checkSensical)
233
+ constant(expr, Literal (Constant (false )))
234
+ else if (testCls.isPrimitiveValueClass)
235
+ if (foundCls.isPrimitiveValueClass)
236
+ constant(expr, Literal (Constant (foundCls == testCls)))
234
237
else
235
- derivedTree(expr, defn.Any_isInstanceOf , testType)
238
+ transformIsInstanceOf(expr, defn.boxedType(testCls.typeRef), flagUnrelated)
239
+ else
240
+ derivedTree(expr, defn.Any_isInstanceOf , testType)
241
+ }
242
+
243
+ def transformAsInstanceOf (testType : Type ): Tree = {
244
+ def testCls = testType.widen.classSymbol
245
+ if (expr.tpe <:< testType)
246
+ Typed (expr, tree.args.head)
247
+ else if (testCls eq defn.BoxedUnitClass ) {
248
+ // as a special case, casting to Unit always successfully returns Unit
249
+ Block (expr :: Nil , Literal (Constant (()))).withSpan(expr.span)
250
+ }
251
+ else if (foundCls.isPrimitiveValueClass) {
252
+ if (testCls.isPrimitiveValueClass) primitiveConversion(expr, testCls)
253
+ else derivedTree(box(expr), defn.Any_asInstanceOf , testType)
236
254
}
255
+ else if (testCls.isPrimitiveValueClass)
256
+ unbox(expr.ensureConforms(defn.ObjectType ), testType)
257
+ else if (isDerivedValueClass(testCls)) {
258
+ expr // adaptToType in Erasure will do the necessary type adaptation
259
+ }
260
+ else if (testCls eq defn.NothingClass ) {
261
+ // In the JVM `x.asInstanceOf[Nothing]` would throw a class cast exception except when `x eq null`.
262
+ // To avoid this loophole we execute `x` and then regardless of the result throw a `ClassCastException`
263
+ val throwCCE = Throw (New (defn.ClassCastExceptionClass .typeRef, defn.ClassCastExceptionClass_stringConstructor ,
264
+ Literal (Constant (" Cannot cast to scala.Nothing" )) :: Nil ))
265
+ Block (expr :: Nil , throwCCE).withSpan(expr.span)
266
+ }
267
+ else
268
+ derivedTree(expr, defn.Any_asInstanceOf , testType)
269
+ }
237
270
238
- def transformAsInstanceOf (testType : Type ): Tree = {
239
- def testCls = testType.widen.classSymbol
240
- if (expr.tpe <:< testType)
241
- Typed (expr, tree.args.head)
242
- else if (testCls eq defn.BoxedUnitClass ) {
243
- // as a special case, casting to Unit always successfully returns Unit
244
- Block (expr :: Nil , Literal (Constant (()))).withSpan(expr.span)
271
+ /** Transform isInstanceOf OrType
272
+ *
273
+ * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
274
+ * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
275
+ *
276
+ * The transform happens before erasure of `testType`, thus cannot be merged
277
+ * with `transformIsInstanceOf`, which depends on erased type of `testType`.
278
+ */
279
+ def transformTypeTest (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = testType.dealias match {
280
+ case _ : SingletonType =>
281
+ expr.isInstance(testType).withSpan(tree.span)
282
+ case OrType (tp1, tp2) =>
283
+ evalOnce(expr) { e =>
284
+ transformTypeTest(e, tp1, flagUnrelated = false )
285
+ .or(transformTypeTest(e, tp2, flagUnrelated = false ))
245
286
}
246
- else if (foundCls.isPrimitiveValueClass) {
247
- if (testCls.isPrimitiveValueClass) primitiveConversion(expr, testCls)
248
- else derivedTree(box(expr), defn.Any_asInstanceOf , testType)
287
+ case AndType (tp1, tp2) =>
288
+ evalOnce(expr) { e =>
289
+ transformTypeTest(e, tp1, flagUnrelated)
290
+ .and(transformTypeTest(e, tp2, flagUnrelated))
249
291
}
250
- else if (testCls.isPrimitiveValueClass)
251
- unbox(expr.ensureConforms(defn.ObjectType ), testType)
252
- else if (isDerivedValueClass(testCls)) {
253
- expr // adaptToType in Erasure will do the necessary type adaptation
292
+ case defn.MultiArrayOf (elem, ndims) if isUnboundedGeneric(elem) =>
293
+ def isArrayTest (arg : Tree ) =
294
+ ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal (Constant (ndims)))
295
+ if (ndims == 1 ) isArrayTest(expr)
296
+ else evalOnce(expr) { e =>
297
+ derivedTree(e, defn.Any_isInstanceOf , e.tpe)
298
+ .and(isArrayTest(e))
254
299
}
255
- else if (testCls eq defn.NothingClass ) {
256
- // In the JVM `x.asInstanceOf[Nothing]` would throw a class cast exception except when `x eq null`.
257
- // To avoid this loophole we execute `x` and then regardless of the result throw a `ClassCastException`
258
- val throwCCE = Throw (New (defn.ClassCastExceptionClass .typeRef, defn.ClassCastExceptionClass_stringConstructor ,
259
- Literal (Constant (" Cannot cast to scala.Nothing" )) :: Nil ))
260
- Block (expr :: Nil , throwCCE).withSpan(expr.span)
261
- }
262
- else
263
- derivedTree(expr, defn.Any_asInstanceOf , testType)
264
- }
265
-
266
- /** Transform isInstanceOf OrType
267
- *
268
- * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
269
- * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
270
- *
271
- * The transform happens before erasure of `testType`, thus cannot be merged
272
- * with `transformIsInstanceOf`, which depends on erased type of `testType`.
273
- */
274
- def transformTypeTest (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = testType.dealias match {
275
- case _ : SingletonType =>
276
- expr.isInstance(testType).withSpan(tree.span)
277
- case OrType (tp1, tp2) =>
278
- evalOnce(expr) { e =>
279
- transformTypeTest(e, tp1, flagUnrelated = false )
280
- .or(transformTypeTest(e, tp2, flagUnrelated = false ))
281
- }
282
- case AndType (tp1, tp2) =>
283
- evalOnce(expr) { e =>
284
- transformTypeTest(e, tp1, flagUnrelated)
285
- .and(transformTypeTest(e, tp2, flagUnrelated))
286
- }
287
- case defn.MultiArrayOf (elem, ndims) if isUnboundedGeneric(elem) =>
288
- def isArrayTest (arg : Tree ) =
289
- ref(defn.runtimeMethodRef(nme.isArray)).appliedTo(arg, Literal (Constant (ndims)))
290
- if (ndims == 1 ) isArrayTest(expr)
291
- else evalOnce(expr) { e =>
292
- derivedTree(e, defn.Any_isInstanceOf , e.tpe)
293
- .and(isArrayTest(e))
294
- }
295
- case _ =>
296
- transformIsInstanceOf(expr, erasure(testType), flagUnrelated)
297
- }
298
-
299
- if (sym.isTypeTest) {
300
- val argType = tree.args.head.tpe
301
- val isTrusted = tree.hasAttachment(PatternMatcher .TrustedTypeTestKey )
302
- if (! isTrusted && ! checkable(expr.tpe, argType, tree.span))
303
- ctx.warning(i " the type test for $argType cannot be checked at runtime " , tree.sourcePos)
304
- transformTypeTest(expr, tree.args.head.tpe, flagUnrelated = true )
305
- }
306
- else if (sym.isTypeCast)
307
- transformAsInstanceOf(erasure(tree.args.head.tpe))
308
- else tree
300
+ case _ =>
301
+ transformIsInstanceOf(expr, erasure(testType), flagUnrelated)
302
+ }
309
303
310
- case _ =>
311
- tree
304
+ if (sym.isTypeTest) {
305
+ val argType = tree.args.head.tpe
306
+ val isTrusted = tree.hasAttachment(PatternMatcher .TrustedTypeTestKey )
307
+ if (! isTrusted && ! checkable(expr.tpe, argType, tree.span))
308
+ ctx.warning(i " the type test for $argType cannot be checked at runtime " , tree.sourcePos)
309
+ transformTypeTest(expr, tree.args.head.tpe, flagUnrelated = true )
310
+ }
311
+ else if (sym.isTypeCast)
312
+ transformAsInstanceOf(erasure(tree.args.head.tpe))
313
+ else tree
312
314
}
313
315
}
314
316
}
0 commit comments