@@ -16,6 +16,8 @@ import util.Spans._
1616import reporting ._
1717import config .Printers .{ transforms => debug }
1818
19+ import patmat .Typ
20+
1921/** This transform normalizes type tests and type casts,
2022 * also replacing type tests with singleton argument type with reference equality check
2123 * Any remaining type tests
@@ -51,7 +53,8 @@ object TypeTestsCasts {
5153 * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
5254 * 7. if `P` is a refinement type, "it's a refinement type"
5355 * 8. if `P` is a local class which is not statically reachable from the scope where `X` is defined, "it's a local class"
54- * 9. otherwise, ""
56+ * 9. if `X` is `T1 | T2`, checkable(T1, P) && checkable(T2, P) or (isCheckDefinitelyFalse(T1, P) && checkable(T2, P)) or (checkable(T1, P) && isCheckDefinitelyFalse(T2, P)).
57+ * 10. otherwise, ""
5558 */
5659 def whyUncheckable (X : Type , P : Type , span : Span )(using Context ): String = atPhase(Phases .refchecksPhase.next) {
5760 extension (inline s1 : String ) inline def && (inline s2 : String ): String = if s1 == " " then s2 else s1
@@ -129,6 +132,41 @@ object TypeTestsCasts {
129132
130133 }
131134
135+ /** Whether the check X.isInstanceOf[P] is definitely false? */
136+ def isCheckDefinitelyFalse (X : Type , P : Type )(using Context ): Boolean = trace(s " isCheckDefinitelyFalse( ${X .show}, ${P .show}) " ) {
137+ X .widenDealias match
138+ case AndType (x1, x2) =>
139+ isCheckDefinitelyFalse(x1, P ) || isCheckDefinitelyFalse(x2, P )
140+
141+ case x =>
142+ P .widenDealias match
143+ case AndType (p1, p2) =>
144+ isCheckDefinitelyFalse(x, p1) || isCheckDefinitelyFalse(x, p2)
145+
146+ case p =>
147+ val pSpace = Typ (p)
148+ val xSpace = Typ (x)
149+ if pSpace.canDecompose then
150+ val ps = pSpace.decompose.map(_.tp)
151+ ps.forall(p => isCheckDefinitelyFalse(x, p))
152+ else if xSpace.canDecompose then
153+ val xs = xSpace.decompose.map(_.tp)
154+ xs.forall(x => isCheckDefinitelyFalse(x, p))
155+ else
156+ if x.typeSymbol.isClass && p.typeSymbol.isClass then
157+ val xClass = effectiveClass(x)
158+ val pClass = effectiveClass(p)
159+
160+ val bothAreClasses = ! xClass.is(Trait ) && ! pClass.is(Trait )
161+ val notXsubP = ! xClass.derivesFrom(pClass)
162+ val notPsubX = ! pClass.derivesFrom(xClass)
163+ bothAreClasses && notXsubP && notPsubX
164+ || xClass.is(Final ) && notXsubP
165+ || pClass.is(Final ) && notPsubX
166+ else
167+ false
168+ }
169+
132170 def recur (X : Type , P : Type ): String = (X <:< P ) ||| (P .dealias match {
133171 case _ : SingletonType => " "
134172 case _ : TypeProxy
@@ -146,7 +184,20 @@ object TypeTestsCasts {
146184 // - T1 <:< T2 | T3
147185 // - T1 & T2 <:< T3
148186 // See TypeComparer#either
149- recur(tp1, P ) && recur(tp2, P )
187+ val res1 = recur(tp1, P )
188+ val res2 = recur(tp2, P )
189+
190+ if res1.isEmpty && res2.isEmpty then
191+ res1
192+ else if res2.isEmpty then
193+ if isCheckDefinitelyFalse(tp1, P ) then res2
194+ else res1
195+ else if res1.isEmpty then
196+ if isCheckDefinitelyFalse(tp2, P ) then res1
197+ else res2
198+ else
199+ res1
200+
150201 case _ =>
151202 // always false test warnings are emitted elsewhere
152203 X .classSymbol.exists && P .classSymbol.exists &&
@@ -302,8 +353,8 @@ object TypeTestsCasts {
302353
303354 /** Transform isInstanceOf
304355 *
305- * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
306- * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
356+ * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
357+ * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
307358 * expr.isInstanceOf[Tuple] ~~> scala.runtime.Tuples.isInstanceOfTuple(expr)
308359 * expr.isInstanceOf[EmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfEmptyTuple(expr)
309360 * expr.isInstanceOf[NonEmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfNonEmptyTuple(expr)
0 commit comments