[FIR] Utilize equality compatibility logic for cast checks

This makes it more consistent and fixes some
overlooked corner cases. Also it was decided
on the last equality applicability DM
(KT-62646) that we'd like
`is`/`!is`/`as`/`as?` to work similarly
to `===`/`!==`.

Also note that it now gives a clearer
explaination of why some corner cases work
the way they do. For example,
`FirPsiDiagnosticTestGenerated.testLambdaInLhsOfTypeOperatorCall`
yields `UNCHECKED_CAST` instead of
`CAST_NEVER_SUCCEEDS`, because
`toTypeInfo()` replaces all type arguments
with star projections, even when the argument
is not a type parameter. This is because
it has been desided to work this way in
KT-57779.

In
`FirPsiOldFrontendDiagnosticsTestGenerated..NeverSucceeds#testNoGenericsRelated`
the diagnostic is introduced, because
`t2 as FC1` and `FC1` is a final class with
no `T5` supertype.

`UNCHECKED_CAST` in
`FirPsiOldFrontendDiagnosticsTestGenerated.testSmartCast`
disappeared, because previously we didn't
take smartcasts into account.

Note that
`FirPsiOldFrontendDiagnosticsTestGenerated.testMappedSubtypes`
is a false positive. It appears because `isSubtypeOf()` doesn't
take into account platform types in supertypes of the given types
(doesn't map them).
This commit is contained in:
Nikolay Lunyak
2024-02-28 14:17:35 +02:00
committed by Space Team
parent a5e43c9e3f
commit fab6cec93a
25 changed files with 253 additions and 193 deletions
@@ -28,7 +28,7 @@ inline fun <reified T> test(x: T?, a: Any) {
a is <!CANNOT_CHECK_FOR_ERASED!>Box<T><!>
a is <!CANNOT_CHECK_FOR_ERASED!>Array<T><!>
a <!UNCHECKED_CAST!>as Box<T><!>
a <!UNCHECKED_CAST!>as Array<T><!>
a <!CAST_NEVER_SUCCEEDS!>as<!> Array<T>
a is <!CANNOT_CHECK_FOR_ERASED!>Box<List<T>><!>
a is <!CANNOT_CHECK_FOR_ERASED!>Array<List<T>><!>
@@ -0,0 +1,21 @@
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -UNUSED_PARAMETER -ABSTRACT_MEMBER_NOT_IMPLEMENTED -USELESS_CAST
import java.lang.CharSequence as JCS
class JSub: JCS
class Sub: CharSequence
fun test(
s: Sub,
js: JSub,
cs: CharSequence,
jcs: JCS
) {
// js as CharSequence // - this case is not supported due to limitation in PlatformToKotlinClassMap
js <!CAST_NEVER_SUCCEEDS!>as<!> JCS
s as CharSequence
s as JCS
js <!CAST_NEVER_SUCCEEDS!>as<!> Sub
s <!CAST_NEVER_SUCCEEDS!>as<!> JSub
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -PLATFORM_CLASS_MAPPED_TO_KOTLIN -UNUSED_PARAMETER -ABSTRACT_MEMBER_NOT_IMPLEMENTED -USELESS_CAST
import java.lang.CharSequence as JCS
@@ -0,0 +1,46 @@
// !DIAGNOSTICS: -USELESS_CAST -UNCHECKED_CAST
interface T1
interface T2
interface T3
open class OC1: T1
open class OC2: OC1(), T2
class FC1: OC2(), T3
interface T4: <!INTERFACE_WITH_SUPERCLASS!>OC1<!>
interface T5: T2
fun <TP1: OC1, TP2: T2, TP3: OC2> test(
t2: T2,
t4: T4,
fc1: FC1,
oc1: OC1,
oc2: OC2,
tp1: TP1,
tp2: TP2
) {
fc1 as FC1
fc1 as OC1
fc1 as T1
fc1 as TP1
oc1 as FC1
oc1 as OC2
oc2 as OC1
oc1 as T2
oc1 as T1
oc1 as TP1
oc1 as TP2
t2 as FC1
t2 as OC2
t4 as OC1
t2 as T2
t2 <!CAST_NEVER_SUCCEEDS!>as<!> T5
t2 as TP2
tp1 as FC1
tp1 as OC1
tp1 as OC2
tp2 as T2
tp2 as T5
tp1 as TP3
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -USELESS_CAST -UNCHECKED_CAST
interface T1
interface T2
@@ -0,0 +1,34 @@
// !DIAGNOSTICS: -UNCHECKED_CAST
interface Trait1
interface Trait2
open class OClass1
open class OClass2
class FClass1
class FClass2
fun <TP1: OClass1, TP2: OClass2> test(
t1: Trait1,
oc1: OClass1,
fc1: FClass1,
tp1: TP1
) {
t1 as Trait2
t1 as OClass2
t1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
t1 as TP2
oc1 as Trait2
oc1 as OClass2
oc1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
oc1 as TP2
fc1 <!CAST_NEVER_SUCCEEDS!>as<!> Trait2
fc1 <!CAST_NEVER_SUCCEEDS!>as<!> OClass2
fc1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
fc1 as TP2
tp1 as Trait2
tp1 as OClass2
tp1 <!CAST_NEVER_SUCCEEDS!>as<!> FClass2
tp1 as TP2
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNCHECKED_CAST
interface Trait1
interface Trait2
@@ -37,7 +37,7 @@ fun <E> foo(x: Any, y: Any) : Any {
y <!UNCHECKED_CAST!>as Outer<*><!>
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer<!>
y <!UNCHECKED_CAST!>as Outer<*>.Inner<!>
y <!CAST_NEVER_SUCCEEDS!>as<!> Outer<*>.Inner
y as <!NO_TYPE_ARGUMENTS_ON_RHS!>Outer.Inner<!>
return C()
@@ -15,7 +15,7 @@ public interface I<K> {}
// FILE: main.kt
fun main(z: I<String>) {
z <!UNCHECKED_CAST!>as Test<Test2<Int, *>><!>
z <!UNCHECKED_CAST!>as Test<Test2<Int, <!UNRESOLVED_REFERENCE!>Foo<!>>><!>
z as Test<Test2<Int, <!UNRESOLVED_REFERENCE!>Foo<!>>>
z as Test<<!UNRESOLVED_REFERENCE!>Foo<!>>
z as <!UNRESOLVED_REFERENCE!>Any2<!>
println(z)
@@ -11,6 +11,6 @@ public class A {
fun main(a: A, ml: Any) {
if (ml is <!CANNOT_CHECK_FOR_ERASED!>MutableList<String><!>) {
a.foo(<!JAVA_TYPE_MISMATCH!>ml<!>)
a.foo(ml <!UNCHECKED_CAST!>as List<Any><!>)
a.foo(ml as List<Any>)
}
}
@@ -16,7 +16,7 @@ fun String?.gav() {}
fun bar(s: String?) {
if (s != null) return
s.gav()
s as? String
s <!USELESS_CAST!>as? String<!>
s as String?
s as String
s <!CAST_NEVER_SUCCEEDS!>as<!> String
}
@@ -43,8 +43,8 @@ fun f(a: SomeClass?) {
// 'aa' cannot be cast to SomeSubClass
aa<!UNSAFE_CALL!>.<!>hashCode()
aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
(aa as SomeSubClass).foo
(aa <!USELESS_CAST!>as? SomeSubClass<!>)<!UNSAFE_CALL!>.<!>foo
(aa <!CAST_NEVER_SUCCEEDS!>as<!> SomeSubClass).foo
}
val b = (aa as? SomeSubClass)?.foo
aa = null
@@ -52,8 +52,8 @@ fun f(a: SomeClass?) {
// 'aa' cannot be cast to SomeSubClass
aa<!UNSAFE_CALL!>.<!>hashCode()
aa.<!UNRESOLVED_REFERENCE!>foo<!>
(aa as? SomeSubClass)<!UNSAFE_CALL!>.<!>foo
(aa as SomeSubClass).foo
(aa <!USELESS_CAST!>as? SomeSubClass<!>)<!UNSAFE_CALL!>.<!>foo
(aa <!CAST_NEVER_SUCCEEDS!>as<!> SomeSubClass).foo
}
aa = a
val c = aa as? SomeSubClass
@@ -55,5 +55,5 @@ fun testMany(a: Any) {
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a<!>)
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a<!>)
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a as Int<!>)
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a as String<!>)
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_ERROR!>a <!CAST_NEVER_SUCCEEDS!>as<!> String<!>)
}
@@ -55,5 +55,5 @@ fun testMany(a: Any) {
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a<!>)
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a<!>)
manyFoo(v = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a as Int<!>)
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a as String<!>)
manyFoo(s = <!ARGUMENT_TYPE_MISMATCH, ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION_WARNING!>a <!CAST_NEVER_SUCCEEDS!>as<!> String<!>)
}
+3 -3
View File
@@ -1,8 +1,8 @@
// !DIAGNOSTICS: -UNCHECKED_CAST
fun <T> Collection<T>.toArray(): Array<T> = this as Array<T>
fun Collection<String>.toArray2(): Array<String> = this as Array<String>
fun <T> toArray3(x: Collection<T>): Array<T> = x as Array<T>
fun <T> Collection<T>.toArray(): Array<T> = this <!CAST_NEVER_SUCCEEDS!>as<!> Array<T>
fun Collection<String>.toArray2(): Array<String> = this <!CAST_NEVER_SUCCEEDS!>as<!> Array<String>
fun <T> toArray3(x: Collection<T>): Array<T> = x <!CAST_NEVER_SUCCEEDS!>as<!> Array<T>
class Foo<T> {
operator fun plus(x: Foo<T>): Array<T> {