@@ -16,6 +16,8 @@ import util.Spans._
16
16
import reporting ._
17
17
import config .Printers .{ transforms => debug }
18
18
19
+ import patmat .Typ
20
+
19
21
/** This transform normalizes type tests and type casts,
20
22
* also replacing type tests with singleton argument type with reference equality check
21
23
* Any remaining type tests
@@ -51,7 +53,8 @@ object TypeTestsCasts {
51
53
* 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
52
54
* 7. if `P` is a refinement type, "it's a refinement type"
53
55
* 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`, (isCheckDefinitelyFalse(T1, P) && checkable(T2, P)) or (checkable(T1, P) && isCheckDefinitelyFalse(T2, P)).
57
+ * 10. otherwise, ""
55
58
*/
56
59
def whyUncheckable (X : Type , P : Type , span : Span )(using Context ): String = atPhase(Phases .refchecksPhase.next) {
57
60
extension (inline s1 : String ) inline def && (inline s2 : String ): String = if s1 == " " then s2 else s1
@@ -129,6 +132,34 @@ object TypeTestsCasts {
129
132
130
133
}
131
134
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 .dealias match
138
+ case AndType (x1, x2) =>
139
+ isCheckDefinitelyFalse(x1, P ) || isCheckDefinitelyFalse(x2, P )
140
+
141
+ case x =>
142
+ P .dealias 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.exists(x => isCheckDefinitelyFalse(x, p))
155
+ else
156
+ val xClass = effectiveClass(x.widen)
157
+ val pClass = effectiveClass(p.widen)
158
+
159
+ ! xClass.derivesFrom(pClass)
160
+ && (xClass.is(Final ) || pClass.is(Final ) || ! xClass.is(Trait ) && ! pClass.is(Trait ))
161
+ }
162
+
132
163
def recur (X : Type , P : Type ): String = (X <:< P ) ||| (P .dealias match {
133
164
case _ : SingletonType => " "
134
165
case _ : TypeProxy
@@ -146,7 +177,14 @@ object TypeTestsCasts {
146
177
// - T1 <:< T2 | T3
147
178
// - T1 & T2 <:< T3
148
179
// See TypeComparer#either
149
- recur(tp1, P ) && recur(tp2, P )
180
+ val res1 = recur(tp1, P )
181
+ val res2 = recur(tp2, P )
182
+
183
+ if res1.isEmpty && res2.isEmpty then res1
184
+ else if isCheckDefinitelyFalse(tp1, P ) && res2.isEmpty then res2
185
+ else if res1.isEmpty && isCheckDefinitelyFalse(tp2, P ) then res1
186
+ else res1
187
+
150
188
case _ =>
151
189
// always false test warnings are emitted elsewhere
152
190
X .classSymbol.exists && P .classSymbol.exists &&
@@ -302,8 +340,8 @@ object TypeTestsCasts {
302
340
303
341
/** Transform isInstanceOf
304
342
*
305
- * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
306
- * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
343
+ * expr.isInstanceOf[A | B] ~~> expr.isInstanceOf[A] | expr.isInstanceOf[B]
344
+ * expr.isInstanceOf[A & B] ~~> expr.isInstanceOf[A] & expr.isInstanceOf[B]
307
345
* expr.isInstanceOf[Tuple] ~~> scala.runtime.Tuples.isInstanceOfTuple(expr)
308
346
* expr.isInstanceOf[EmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfEmptyTuple(expr)
309
347
* expr.isInstanceOf[NonEmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfNonEmptyTuple(expr)
0 commit comments