Files
kotlin-fork/compiler/testData/diagnostics/tests/visibility/moreSpecificProtectedSimple.kt
T
Denis.Zharkov b6b132a9a3 K2: Avoid false-positive overload resolution ambiguity with smart casts
The idea is that when we have successful candidates both from smart cast
and original type, we should discriminate in the favor of former ones.

While this problem (see kt55722.kt) existed before this branch is merged,
initially it was recognized on FP Ultimate when we stopped assuming
captured types from the same projections as equal (see kt55722Initial.kt).

^KT-55722 Fixed
^KT-55024 Fixed
^KT-56283 Related
^KT-56310 Related
2023-02-15 08:13:54 +00:00

56 lines
2.1 KiB
Kotlin
Vendored

// SKIP_TXT
// FIR_DUMP
// ISSUE: KT-56310
interface Base
interface Derived : Base
interface M1 {
val success: Boolean
}
interface M1Sub : M1
open class A {
open protected fun baz(a: Derived): M1 = TODO()
open protected fun foo(a: Derived): M1 = TODO()
fun f(a: A, b: B, d: Derived) {
a.baz(d).success // OK in K1 and K2
a.foo(d).success // OK in K1 and K2
// Both K1 and K2 resolves calls to B's members with String return type because other's members in B type are invisible
b.baz(d).length
b.foo(d).length
when (a) {
is B -> {
// OK in K1 because `a.baz(d)` resolved to String returning methid (just as `b.baz(d)`)
<!DEBUG_INFO_SMARTCAST!>a<!>.baz(d).length
// OK in K2, because we have two visible candidates:
// - A::baz from original (after unwrapping smart cast) receiver
// - B::baz that returns String
// But the first one is more specific via its parameter types
<!DEBUG_INFO_SMARTCAST!>a<!>.baz(d).<!UNRESOLVED_REFERENCE!>success<!> // Unresolved reference in K1, going to be OK in K2
// Works in K2 for the same reasons as `a.baz(d)`
// In K1, works by coincidence because we bind override groups for members from original and smart cast receiver,
// and as return type from B is the same (not more specific), we choose the member from A as a group representative.
// Thus, we have successful candidates
// - A::foo
// - B::foo returning String
// But A::foo is more specific, so we choose it
a.foo(d).success
}
}
}
}
class B : A() {
override fun baz(a: Derived): M1Sub = TODO()
public fun baz(a: Base): String = TODO()
// The only difference between `baz` and `foo` is that the former has more specific covariant return type (M1Sub)
override fun foo(a: Derived): M1 = TODO()
public fun foo(a: Base): String = TODO()
}