@@ -53,7 +53,7 @@ object TypeTestsCasts {
53
53
* 7. if `P` is a refinement type, FALSE
54
54
* 8. otherwise, TRUE
55
55
*/
56
- def checkable (X : Type , P : Type , span : Span )(implicit ctx : Context ): Boolean = {
56
+ def checkable (X : Type , P : Type , span : Span )(using Context ): Boolean = {
57
57
def isAbstract (P : Type ) = ! P .dealias.typeSymbol.isClass
58
58
def isPatternTypeSymbol (sym : Symbol ) = ! sym.isClass && sym.is(Case )
59
59
@@ -155,7 +155,7 @@ object TypeTestsCasts {
155
155
res
156
156
}
157
157
158
- def interceptTypeApply (tree : TypeApply )(implicit ctx : Context ): Tree = trace(s " transforming ${tree.show}" , show = true ) {
158
+ def interceptTypeApply (tree : TypeApply )(using Context ): Tree = trace(s " transforming ${tree.show}" , show = true ) {
159
159
/** Intercept `expr.xyz[XYZ]` */
160
160
def interceptWith (expr : Tree ): Tree =
161
161
if (expr.isEmpty) tree
@@ -172,7 +172,9 @@ object TypeTestsCasts {
172
172
else if tp.isRef(defn.AnyValClass ) then defn.AnyClass
173
173
else tp.classSymbol
174
174
175
- def foundCls = effectiveClass(expr.tpe.widen)
175
+ def foundClasses (tp : Type , acc : List [Symbol ]): List [Symbol ] = tp match
176
+ case OrType (tp1, tp2) => foundClasses(tp2, foundClasses(tp1, acc))
177
+ case _ => effectiveClass(tp) :: acc
176
178
177
179
def inMatch =
178
180
tree.fun.symbol == defn.Any_typeTest || // new scheme
@@ -181,7 +183,7 @@ object TypeTestsCasts {
181
183
def transformIsInstanceOf (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = {
182
184
def testCls = effectiveClass(testType.widen)
183
185
184
- def unreachable (why : => String ): Boolean = {
186
+ def unreachable (why : => String )( using ctx : Context ) : Boolean = {
185
187
if (flagUnrelated)
186
188
if (inMatch) ctx.error(em " this case is unreachable since $why" , expr.sourcePos)
187
189
else ctx.warning(em " this will always yield false since $why" , expr.sourcePos)
@@ -191,7 +193,7 @@ object TypeTestsCasts {
191
193
/** Are `foundCls` and `testCls` classes that allow checks
192
194
* whether a test would be always false?
193
195
*/
194
- def isCheckable =
196
+ def isCheckable ( foundCls : Symbol ) =
195
197
foundCls.isClass && testCls.isClass &&
196
198
! (testCls.isPrimitiveValueClass && ! foundCls.isPrimitiveValueClass) &&
197
199
// if `test` is primitive but `found` is not, we might have a case like
@@ -203,8 +205,8 @@ object TypeTestsCasts {
203
205
/** Check whether a runtime test that a value of `foundCls` can be a `testCls`
204
206
* can be true in some cases. Issues a warning or an error otherwise.
205
207
*/
206
- def checkSensical : Boolean =
207
- if (! isCheckable) true
208
+ def checkSensical ( foundCls : Symbol )( using Context ) : Boolean =
209
+ if (! isCheckable(foundCls) ) true
208
210
else if (foundCls.isPrimitiveValueClass && ! testCls.isPrimitiveValueClass) {
209
211
ctx.error(" cannot test if value types are references" , tree.sourcePos)
210
212
false
@@ -214,38 +216,50 @@ object TypeTestsCasts {
214
216
testCls.is(Final ) || ! testCls.is(Trait ) && ! foundCls.is(Trait )
215
217
)
216
218
if (foundCls.is(Final ))
217
- unreachable(i " $foundCls is not a subclass of $testCls" )
219
+ unreachable(i " type ${expr.tpe.widen} is not a subclass of $testCls" )
218
220
else if (unrelated)
219
- unreachable(i " $foundCls and $testCls are unrelated " )
221
+ unreachable(i " type ${expr.tpe.widen} and $testCls are unrelated " )
220
222
else true
221
223
}
222
224
else true
223
225
224
226
if (expr.tpe <:< testType)
225
227
if (expr.tpe.isNotNull) {
226
- if (! inMatch) ctx.warning(TypeTestAlwaysSucceeds (foundCls, testCls ), tree.sourcePos)
228
+ if (! inMatch) ctx.warning(TypeTestAlwaysSucceeds (expr.tpe, testType ), tree.sourcePos)
227
229
constant(expr, Literal (Constant (true )))
228
230
}
229
231
else expr.testNotNull
230
- else if (! checkSensical)
231
- constant(expr, Literal (Constant (false )))
232
- else if (testCls.isPrimitiveValueClass)
233
- if (foundCls.isPrimitiveValueClass)
234
- constant(expr, Literal (Constant (foundCls == testCls)))
232
+ else {
233
+ val nestedCtx = ctx.fresh.setNewTyperState()
234
+ val foundClsSyms = foundClasses(expr.tpe.widen, Nil )
235
+ val sensical = foundClsSyms.exists(sym => checkSensical(sym)(using nestedCtx))
236
+ if (! sensical) {
237
+ nestedCtx.typerState.commit()
238
+ constant(expr, Literal (Constant (false )))
239
+ }
240
+ else if (testCls.isPrimitiveValueClass)
241
+ foundClsSyms match
242
+ case List (cls) if cls.isPrimitiveValueClass =>
243
+ constant(expr, Literal (Constant (foundClsSyms.head == testCls)))
244
+ case _ =>
245
+ transformIsInstanceOf(expr, defn.boxedType(testCls.typeRef), flagUnrelated)
235
246
else
236
- transformIsInstanceOf(expr, defn.boxedType(testCls.typeRef), flagUnrelated)
237
- else
238
- derivedTree(expr, defn.Any_isInstanceOf , testType)
247
+ derivedTree(expr, defn.Any_isInstanceOf , testType)
248
+ }
239
249
}
240
250
241
251
def transformAsInstanceOf (testType : Type ): Tree = {
242
- def testCls = testType.widen.classSymbol
252
+ def testCls = effectiveClass(testType.widen)
253
+ def foundClsSymPrimitive = {
254
+ val foundClsSyms = foundClasses(expr.tpe.widen, Nil )
255
+ foundClsSyms.size == 1 && foundClsSyms.head.isPrimitiveValueClass
256
+ }
243
257
if (erasure(expr.tpe) <:< testType)
244
258
Typed (expr, tree.args.head) // Replace cast by type ascription (which does not generate any bytecode)
245
259
else if (testCls eq defn.BoxedUnitClass )
246
260
// as a special case, casting to Unit always successfully returns Unit
247
261
Block (expr :: Nil , Literal (Constant (()))).withSpan(expr.span)
248
- else if (foundCls.isPrimitiveValueClass )
262
+ else if (foundClsSymPrimitive )
249
263
if (testCls.isPrimitiveValueClass) primitiveConversion(expr, testCls)
250
264
else derivedTree(box(expr), defn.Any_asInstanceOf , testType)
251
265
else if (testCls.isPrimitiveValueClass)
0 commit comments