@@ -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`, checkable(T1, P) && checkable(T2, P) or (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,41 @@ 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 .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
+
132
170
def recur (X : Type , P : Type ): String = (X <:< P ) ||| (P .dealias match {
133
171
case _ : SingletonType => " "
134
172
case _ : TypeProxy
@@ -146,7 +184,20 @@ object TypeTestsCasts {
146
184
// - T1 <:< T2 | T3
147
185
// - T1 & T2 <:< T3
148
186
// 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
+
150
201
case _ =>
151
202
// always false test warnings are emitted elsewhere
152
203
X .classSymbol.exists && P .classSymbol.exists &&
@@ -302,8 +353,8 @@ object TypeTestsCasts {
302
353
303
354
/** Transform isInstanceOf
304
355
*
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]
307
358
* expr.isInstanceOf[Tuple] ~~> scala.runtime.Tuples.isInstanceOfTuple(expr)
308
359
* expr.isInstanceOf[EmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfEmptyTuple(expr)
309
360
* expr.isInstanceOf[NonEmptyTuple] ~~> scala.runtime.Tuples.isInstanceOfNonEmptyTuple(expr)
0 commit comments