@@ -38,20 +38,24 @@ object TypeTestsCasts {
38
38
*
39
39
* Then check:
40
40
*
41
- * 1. if `X <:< P`, TRUE
42
- * 2. if `P` is a singleton type, TRUE
43
- * 3. if `P` refers to an abstract type member or type parameter, FALSE
41
+ * 1. if `X <:< P`, ""
42
+ * 2. if `P` is a singleton type, ""
43
+ * 3. if `P` refers to an abstract type member or type parameter, "it refers to an abstract type member or type parameter"
44
44
* 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`.
45
45
* 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`:
46
46
* (a) replace `Ts` with fresh type variables `Xs`
47
47
* (b) constrain `Xs` with `pre.F[Xs] <:< X`
48
- * (c) maximize `pre.F[Xs]` and check `pre.F[Xs] <:< P`
48
+ * (c) maximize `pre.F[Xs]`
49
+ * (d) if !`pre.F[Xs] <:< P`, "its type arguments can't be determined from $X"
49
50
* 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2).
50
- * 7. if `P` is a refinement type, FALSE
51
- * 8. if `P` is a local class which is not statically reachable from the scope where `X` is defined, FALSE
52
- * 9. otherwise, TRUE
51
+ * 7. if `P` is a refinement type, "it's a refinement type"
52
+ * 8. if `P` is a local class which is not statically reachable from the scope where `X` is defined, "it's a local class"
53
+ * 9. otherwise, ""
53
54
*/
54
- def checkable (X : Type , P : Type , span : Span )(using Context ): Boolean = atPhase(Phases .refchecksPhase.next) {
55
+ def checkable (X : Type , P : Type , span : Span )(using Context ): String = atPhase(Phases .refchecksPhase.next) {
56
+ extension (inline s1 : String ) inline def && (inline s2 : String ): String = if s1 == " " then s2 else s1
57
+ extension (inline b : Boolean ) inline def ||| (inline s : String ): String = if b then " " else s
58
+
55
59
// Run just before ElimOpaque transform (which follows RefChecks)
56
60
def isAbstract (P : Type ) = ! P .dealias.typeSymbol.isClass
57
61
@@ -124,10 +128,10 @@ object TypeTestsCasts {
124
128
125
129
}
126
130
127
- def recur (X : Type , P : Type ): Boolean = (X <:< P ) || (P .dealias match {
128
- case _ : SingletonType => true
131
+ def recur (X : Type , P : Type ): String = (X <:< P ) | || (P .dealias match {
132
+ case _ : SingletonType => " "
129
133
case _ : TypeProxy
130
- if isAbstract(P ) => false
134
+ if isAbstract(P ) => i " it refers to an abstract type member or type parameter "
131
135
case defn.ArrayOf (tpT) =>
132
136
X match {
133
137
case defn.ArrayOf (tpE) => recur(tpE, tpT)
@@ -147,21 +151,23 @@ object TypeTestsCasts {
147
151
X .classSymbol.exists && P .classSymbol.exists &&
148
152
! X .classSymbol.asClass.mayHaveCommonChild(P .classSymbol.asClass)
149
153
|| typeArgsTrivial(X , tpe)
154
+ ||| i " its type arguments can't be determined from $X"
150
155
}
151
156
case AndType (tp1, tp2) => recur(X , tp1) && recur(X , tp2)
152
157
case OrType (tp1, tp2) => recur(X , tp1) && recur(X , tp2)
153
158
case AnnotatedType (t, _) => recur(X , t)
154
- case tp2 : RefinedType => recur(X , tp2.parent) && TypeComparer .hasMatchingMember(tp2.refinedName, X , tp2)
159
+ case tp2 : RefinedType => recur(X , tp2.parent)
160
+ && (TypeComparer .hasMatchingMember(tp2.refinedName, X , tp2) ||| i " it's a refinement type " )
155
161
case tp2 : RecType => recur(X , tp2.parent)
156
162
case _
157
163
if P .classSymbol.isLocal && foundClasses(X ).exists(P .classSymbol.isInaccessibleChildOf) => // 8
158
- false
159
- case _ => true
164
+ i " it's a local class "
165
+ case _ => " "
160
166
})
161
167
162
- val res = X .widenTermRefExpr.hasAnnotation(defn. UncheckedAnnot ) || recur(X .widen, replaceP(P ))
168
+ val res = recur(X .widen, replaceP(P ))
163
169
164
- debug.println(i " checking ${ X .show} isInstanceOf ${ P } = $res" )
170
+ debug.println(i " checking $X isInstanceOf $P = $res" )
165
171
166
172
res
167
173
}
@@ -348,9 +354,11 @@ object TypeTestsCasts {
348
354
if (sym.isTypeTest) {
349
355
val argType = tree.args.head.tpe
350
356
val isTrusted = tree.hasAttachment(PatternMatcher .TrustedTypeTestKey )
351
- if (! isTrusted && ! checkable(expr.tpe, argType, tree.span))
352
- report.uncheckedWarning(i " the type test for $argType cannot be checked at runtime " , expr.srcPos)
353
- transformTypeTest(expr, tree.args.head.tpe,
357
+ val unchecked = expr.tpe.widenTermRefExpr.hasAnnotation(defn.UncheckedAnnot )
358
+ val uncheckable = if isTrusted || unchecked then " " else checkable(expr.tpe, argType, tree.span)
359
+ if (uncheckable != " " )
360
+ report.uncheckedWarning(i " the type test for $argType cannot be checked at runtime because $uncheckable" , expr.srcPos)
361
+ transformTypeTest(expr, argType,
354
362
flagUnrelated = enclosingInlineds.isEmpty) // if test comes from inlined code, dont't flag it even if it always false
355
363
}
356
364
else if (sym.isTypeCast)
0 commit comments