@@ -38,20 +38,24 @@ object TypeTestsCasts {
3838 *
3939 * Then check:
4040 *
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"
4444 * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`.
4545 * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`:
4646 * (a) replace `Ts` with fresh type variables `Xs`
4747 * (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"
4950 * 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, ""
5354 */
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+
5559 // Run just before ElimOpaque transform (which follows RefChecks)
5660 def isAbstract (P : Type ) = ! P .dealias.typeSymbol.isClass
5761
@@ -124,10 +128,10 @@ object TypeTestsCasts {
124128
125129 }
126130
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 => " "
129133 case _ : TypeProxy
130- if isAbstract(P ) => false
134+ if isAbstract(P ) => i " it refers to an abstract type member or type parameter "
131135 case defn.ArrayOf (tpT) =>
132136 X match {
133137 case defn.ArrayOf (tpE) => recur(tpE, tpT)
@@ -147,21 +151,23 @@ object TypeTestsCasts {
147151 X .classSymbol.exists && P .classSymbol.exists &&
148152 ! X .classSymbol.asClass.mayHaveCommonChild(P .classSymbol.asClass)
149153 || typeArgsTrivial(X , tpe)
154+ ||| i " its type arguments can't be determined from $X"
150155 }
151156 case AndType (tp1, tp2) => recur(X , tp1) && recur(X , tp2)
152157 case OrType (tp1, tp2) => recur(X , tp1) && recur(X , tp2)
153158 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 " )
155161 case tp2 : RecType => recur(X , tp2.parent)
156162 case _
157163 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 _ => " "
160166 })
161167
162- val res = X .widenTermRefExpr.hasAnnotation(defn. UncheckedAnnot ) || recur(X .widen, replaceP(P ))
168+ val res = recur(X .widen, replaceP(P ))
163169
164- debug.println(i " checking ${ X .show} isInstanceOf ${ P } = $res" )
170+ debug.println(i " checking $X isInstanceOf $P = $res" )
165171
166172 res
167173 }
@@ -348,9 +354,11 @@ object TypeTestsCasts {
348354 if (sym.isTypeTest) {
349355 val argType = tree.args.head.tpe
350356 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,
354362 flagUnrelated = enclosingInlineds.isEmpty) // if test comes from inlined code, dont't flag it even if it always false
355363 }
356364 else if (sym.isTypeCast)
0 commit comments