@@ -53,7 +53,7 @@ object TypeTestsCasts {
5353 * 7. if `P` is a refinement type, FALSE
5454 * 8. otherwise, TRUE
5555 */
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 = {
5757 def isAbstract (P : Type ) = ! P .dealias.typeSymbol.isClass
5858 def isPatternTypeSymbol (sym : Symbol ) = ! sym.isClass && sym.is(Case )
5959
@@ -155,7 +155,7 @@ object TypeTestsCasts {
155155 res
156156 }
157157
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 ) {
159159 /** Intercept `expr.xyz[XYZ]` */
160160 def interceptWith (expr : Tree ): Tree =
161161 if (expr.isEmpty) tree
@@ -172,7 +172,9 @@ object TypeTestsCasts {
172172 else if tp.isRef(defn.AnyValClass ) then defn.AnyClass
173173 else tp.classSymbol
174174
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
176178
177179 def inMatch =
178180 tree.fun.symbol == defn.Any_typeTest || // new scheme
@@ -181,7 +183,7 @@ object TypeTestsCasts {
181183 def transformIsInstanceOf (expr : Tree , testType : Type , flagUnrelated : Boolean ): Tree = {
182184 def testCls = effectiveClass(testType.widen)
183185
184- def unreachable (why : => String ): Boolean = {
186+ def unreachable (why : => String )( using ctx : Context ) : Boolean = {
185187 if (flagUnrelated)
186188 if (inMatch) ctx.error(em " this case is unreachable since $why" , expr.sourcePos)
187189 else ctx.warning(em " this will always yield false since $why" , expr.sourcePos)
@@ -191,7 +193,7 @@ object TypeTestsCasts {
191193 /** Are `foundCls` and `testCls` classes that allow checks
192194 * whether a test would be always false?
193195 */
194- def isCheckable =
196+ def isCheckable ( foundCls : Symbol ) =
195197 foundCls.isClass && testCls.isClass &&
196198 ! (testCls.isPrimitiveValueClass && ! foundCls.isPrimitiveValueClass) &&
197199 // if `test` is primitive but `found` is not, we might have a case like
@@ -203,8 +205,8 @@ object TypeTestsCasts {
203205 /** Check whether a runtime test that a value of `foundCls` can be a `testCls`
204206 * can be true in some cases. Issues a warning or an error otherwise.
205207 */
206- def checkSensical : Boolean =
207- if (! isCheckable) true
208+ def checkSensical ( foundCls : Symbol )( using Context ) : Boolean =
209+ if (! isCheckable(foundCls) ) true
208210 else if (foundCls.isPrimitiveValueClass && ! testCls.isPrimitiveValueClass) {
209211 ctx.error(" cannot test if value types are references" , tree.sourcePos)
210212 false
@@ -214,38 +216,50 @@ object TypeTestsCasts {
214216 testCls.is(Final ) || ! testCls.is(Trait ) && ! foundCls.is(Trait )
215217 )
216218 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" )
218220 else if (unrelated)
219- unreachable(i " $foundCls and $testCls are unrelated " )
221+ unreachable(i " type ${expr.tpe.widen} and $testCls are unrelated " )
220222 else true
221223 }
222224 else true
223225
224226 if (expr.tpe <:< testType)
225227 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)
227229 constant(expr, Literal (Constant (true )))
228230 }
229231 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)
235246 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+ }
239249 }
240250
241251 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+ }
243257 if (erasure(expr.tpe) <:< testType)
244258 Typed (expr, tree.args.head) // Replace cast by type ascription (which does not generate any bytecode)
245259 else if (testCls eq defn.BoxedUnitClass )
246260 // as a special case, casting to Unit always successfully returns Unit
247261 Block (expr :: Nil , Literal (Constant (()))).withSpan(expr.span)
248- else if (foundCls.isPrimitiveValueClass )
262+ else if (foundClsSymPrimitive )
249263 if (testCls.isPrimitiveValueClass) primitiveConversion(expr, testCls)
250264 else derivedTree(box(expr), defn.Any_asInstanceOf , testType)
251265 else if (testCls.isPrimitiveValueClass)
0 commit comments