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
This commit is contained in:
committed by
Space Team
parent
7b6c6fceb6
commit
b6b132a9a3
@@ -0,0 +1,26 @@
|
||||
// SKIP_TXT
|
||||
// FIR_DUMP
|
||||
open class Base
|
||||
class Derived : Base()
|
||||
|
||||
open class A(protected open val foo: Base) {
|
||||
|
||||
protected open fun bar(): Base = Base()
|
||||
|
||||
fun f(other: A) {
|
||||
other.foo // OK in K1 and K2
|
||||
other.bar() // OK in K1 and K2
|
||||
|
||||
when (other) {
|
||||
is B -> {
|
||||
// OK in K2, INVISIBLE_MEMBER (B::foo) in K1
|
||||
other.foo
|
||||
other.bar()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B(override val foo: Derived): A(foo) {
|
||||
override fun bar(): Derived = Derived()
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
FILE: kt56283.fir.kt
|
||||
public open class Base : R|kotlin/Any| {
|
||||
public constructor(): R|Base| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final class Derived : R|Base| {
|
||||
public constructor(): R|Derived| {
|
||||
super<R|Base|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class A : R|kotlin/Any| {
|
||||
public constructor(foo: R|Base|): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
protected open val foo: R|Base| = R|<local>/foo|
|
||||
protected get(): R|Base|
|
||||
|
||||
protected open fun bar(): R|Base| {
|
||||
^bar R|/Base.Base|()
|
||||
}
|
||||
|
||||
public final fun f(other: R|A|): R|kotlin/Unit| {
|
||||
R|<local>/other|.R|/A.foo|
|
||||
R|<local>/other|.R|/A.bar|()
|
||||
when (R|<local>/other|) {
|
||||
($subj$ is R|B|) -> {
|
||||
R|<local>/other|.R|/A.foo|
|
||||
R|<local>/other|.R|/A.bar|()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final class B : R|A| {
|
||||
public constructor(foo: R|Derived|): R|B| {
|
||||
super<R|A|>(R|<local>/foo|)
|
||||
}
|
||||
|
||||
protected open override val foo: R|Derived| = R|<local>/foo|
|
||||
protected get(): R|Derived|
|
||||
|
||||
protected open override fun bar(): R|Derived| {
|
||||
^bar R|/Derived.Derived|()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// SKIP_TXT
|
||||
// FIR_DUMP
|
||||
open class Base
|
||||
class Derived : Base()
|
||||
|
||||
open class A(protected open val foo: Base) {
|
||||
|
||||
protected open fun bar(): Base = Base()
|
||||
|
||||
fun f(other: A) {
|
||||
other.foo // OK in K1 and K2
|
||||
other.bar() // OK in K1 and K2
|
||||
|
||||
when (other) {
|
||||
is B -> {
|
||||
// OK in K2, INVISIBLE_MEMBER (B::foo) in K1
|
||||
<!DEBUG_INFO_SMARTCAST!>other<!>.<!INVISIBLE_MEMBER!>foo<!>
|
||||
<!DEBUG_INFO_SMARTCAST!>other<!>.<!INVISIBLE_MEMBER!>bar<!>()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class B(override val foo: Derived): A(foo) {
|
||||
override fun bar(): Derived = Derived()
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// SKIP_TXT
|
||||
// FIR_DUMP
|
||||
// CHECK_TYPE
|
||||
|
||||
open class Base
|
||||
class Derived : Base()
|
||||
|
||||
interface M1
|
||||
interface M2
|
||||
interface M2Sub : M2
|
||||
interface M3
|
||||
interface M3Sub : M3
|
||||
interface M4
|
||||
|
||||
|
||||
interface M5
|
||||
interface M5Sub : M5
|
||||
interface M5SubSub : M5Sub
|
||||
interface M6
|
||||
|
||||
open class A {
|
||||
public fun foo(a1: Base, a2: Base): M1 = TODO()
|
||||
protected open fun foo(a1: Base, a2: Derived): M2 = TODO()
|
||||
protected open fun foo(a1: Derived, a2: Derived): M3 = TODO()
|
||||
|
||||
public fun baz(a1: Base, a2: Base): M4 = TODO()
|
||||
protected open fun baz(a1: Derived, a2: Derived): M5 = TODO()
|
||||
}
|
||||
|
||||
open class B : A() {
|
||||
public val fromB: Any = Any()
|
||||
|
||||
override fun foo(a1: Base, a2: Derived): M2Sub = TODO()
|
||||
|
||||
override fun baz(a1: Derived, a2: Derived): M5Sub = TODO()
|
||||
|
||||
fun f(a: A, b: B, c: C, d: Derived) {
|
||||
// Only visible is M1
|
||||
a.foo(d, d) checkType { _<M1>() }
|
||||
// We may call M3 and it's the most specific by its parameters
|
||||
b.foo(d, d) checkType { _<M3>() }
|
||||
// We can't call M3 (and M3Sub) as it's protected overriden in C, so it's invisible because
|
||||
// we only allow to call protected members on dispatch receiver values that are subtypes of a dispatch receiver parameter
|
||||
// So, the next visible and specific member is M2Sub
|
||||
c.foo(d, d) checkType { _<M2Sub>() }
|
||||
|
||||
// Only visible is M4
|
||||
a.baz(d, d) checkType { _<M4>() }
|
||||
// M5Sub is more specific and visible for `b` receiver
|
||||
b.baz(d, d) checkType { _<M5Sub>() }
|
||||
|
||||
// M5SubSub is invisible because it's protected in something that is not our super-class
|
||||
// M6 and M1 are visible, but M6 is more specific
|
||||
c.baz(d, d) checkType { _<M6>() }
|
||||
|
||||
when (a) {
|
||||
is C -> {
|
||||
// Make sure smart cast works
|
||||
a.fromC
|
||||
// The same logic as for `c.foo`
|
||||
a.foo(d, d) checkType { _<M2Sub>() }
|
||||
|
||||
// The same logic as for `c.baz`
|
||||
a.baz(d, d) checkType { _<M6>() }
|
||||
}
|
||||
is B -> {
|
||||
// Make sure smart cast works
|
||||
a.fromB
|
||||
// The same logic as `b.foo`
|
||||
a.foo(d, d) checkType { _<M3>() }
|
||||
|
||||
// The same logic as for `b.baz`
|
||||
a.baz(d, d) checkType { _<M5Sub>() }
|
||||
}
|
||||
}
|
||||
|
||||
when (b) {
|
||||
is C -> {
|
||||
b.fromC
|
||||
// In K1, it works just as `c.foo`
|
||||
b.foo(d, d) checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><M2Sub>() }
|
||||
// In K2, M3Sub is invisible, but we have candidate M3 from original receiver and we choose it
|
||||
// Unlike the case of `c.foo` when we choose M2Sub because we don't have more special M3 there in the scope of C
|
||||
// (in the meaning of overload comparison by the value parameter types)
|
||||
b.foo(d, d) checkType { _<M3>() }
|
||||
|
||||
// In K1, it works just as `c.foo`
|
||||
b.baz(d, d) checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><M6>() }
|
||||
// In K2, M5SubSub is invisible, but we have candidate M5Sub from original receiver and we choose it
|
||||
// Unlike the case of `c.baz` when we choose M6 because we don't have more special M5Sub there in the scope of C
|
||||
// (in the meaning of overload comparison by the value parameter types)
|
||||
b.baz(d, d) checkType { _<M5Sub>() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class C : B() {
|
||||
public val fromC: Any = Any()
|
||||
|
||||
override fun foo(a1: Derived, a2: Derived): M3Sub = TODO()
|
||||
|
||||
override fun baz(a1: Derived, a2: Derived): M5SubSub = TODO()
|
||||
// public fallback
|
||||
public fun baz(a1: Derived, a2: Base): M6 = TODO()
|
||||
}
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
FILE: moreSpecificProtected.fir.kt
|
||||
public open class Base : R|kotlin/Any| {
|
||||
public constructor(): R|Base| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public final class Derived : R|Base| {
|
||||
public constructor(): R|Derived| {
|
||||
super<R|Base|>()
|
||||
}
|
||||
|
||||
}
|
||||
public abstract interface M1 : R|kotlin/Any| {
|
||||
}
|
||||
public abstract interface M2 : R|kotlin/Any| {
|
||||
}
|
||||
public abstract interface M2Sub : R|M2| {
|
||||
}
|
||||
public abstract interface M3 : R|kotlin/Any| {
|
||||
}
|
||||
public abstract interface M3Sub : R|M3| {
|
||||
}
|
||||
public abstract interface M4 : R|kotlin/Any| {
|
||||
}
|
||||
public abstract interface M5 : R|kotlin/Any| {
|
||||
}
|
||||
public abstract interface M5Sub : R|M5| {
|
||||
}
|
||||
public abstract interface M5SubSub : R|M5Sub| {
|
||||
}
|
||||
public abstract interface M6 : R|kotlin/Any| {
|
||||
}
|
||||
public open class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun foo(a1: R|Base|, a2: R|Base|): R|M1| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
protected open fun foo(a1: R|Base|, a2: R|Derived|): R|M2| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
protected open fun foo(a1: R|Derived|, a2: R|Derived|): R|M3| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
public final fun baz(a1: R|Base|, a2: R|Base|): R|M4| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
protected open fun baz(a1: R|Derived|, a2: R|Derived|): R|M5| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
}
|
||||
public open class B : R|A| {
|
||||
public constructor(): R|B| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
public final val fromB: R|kotlin/Any| = R|kotlin/Any.Any|()
|
||||
public get(): R|kotlin/Any|
|
||||
|
||||
protected open override fun foo(a1: R|Base|, a2: R|Derived|): R|M2Sub| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
protected open override fun baz(a1: R|Derived|, a2: R|Derived|): R|M5Sub| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
public final fun f(a: R|A|, b: R|B|, c: R|C|, d: R|Derived|): R|kotlin/Unit| {
|
||||
R|<local>/a|.R|/A.foo|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M1|>(checkType@fun R|CheckTypeInv<M1>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M1|>()
|
||||
}
|
||||
)
|
||||
R|<local>/b|.R|/A.foo|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M3|>(checkType@fun R|CheckTypeInv<M3>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M3|>()
|
||||
}
|
||||
)
|
||||
R|<local>/c|.R|/B.foo|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M2Sub|>(checkType@fun R|CheckTypeInv<M2Sub>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M2Sub|>()
|
||||
}
|
||||
)
|
||||
R|<local>/a|.R|/A.baz|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M4|>(checkType@fun R|CheckTypeInv<M4>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M4|>()
|
||||
}
|
||||
)
|
||||
R|<local>/b|.R|/B.baz|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M5Sub|>(checkType@fun R|CheckTypeInv<M5Sub>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M5Sub|>()
|
||||
}
|
||||
)
|
||||
R|<local>/c|.R|/C.baz|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M6|>(checkType@fun R|CheckTypeInv<M6>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M6|>()
|
||||
}
|
||||
)
|
||||
when (R|<local>/a|) {
|
||||
($subj$ is R|C|) -> {
|
||||
R|<local>/a|.R|/C.fromC|
|
||||
R|<local>/a|.R|/B.foo|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M2Sub|>(checkType@fun R|CheckTypeInv<M2Sub>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M2Sub|>()
|
||||
}
|
||||
)
|
||||
R|<local>/a|.R|/C.baz|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M6|>(checkType@fun R|CheckTypeInv<M6>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M6|>()
|
||||
}
|
||||
)
|
||||
}
|
||||
($subj$ is R|B|) -> {
|
||||
R|<local>/a|.R|/B.fromB|
|
||||
R|<local>/a|.R|/A.foo|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M3|>(checkType@fun R|CheckTypeInv<M3>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M3|>()
|
||||
}
|
||||
)
|
||||
R|<local>/a|.R|/B.baz|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M5Sub|>(checkType@fun R|CheckTypeInv<M5Sub>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M5Sub|>()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
when (R|<local>/b|) {
|
||||
($subj$ is R|C|) -> {
|
||||
R|<local>/b|.R|/C.fromC|
|
||||
R|<local>/b|.R|/A.foo|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M3|>(checkType@fun R|CheckTypeInv<M3>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_<None of the following candidates is applicable because of receiver type mismatch: [/_]>#|<R|M2Sub|>()
|
||||
}
|
||||
)
|
||||
R|<local>/b|.R|/A.foo|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M3|>(checkType@fun R|CheckTypeInv<M3>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M3|>()
|
||||
}
|
||||
)
|
||||
R|<local>/b|.R|/B.baz|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M5Sub|>(checkType@fun R|CheckTypeInv<M5Sub>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_<None of the following candidates is applicable because of receiver type mismatch: [/_]>#|<R|M6|>()
|
||||
}
|
||||
)
|
||||
R|<local>/b|.R|/B.baz|(R|<local>/d|, R|<local>/d|).R|/checkType|<R|M5Sub|>(checkType@fun R|CheckTypeInv<M5Sub>|.<anonymous>(): R|kotlin/Unit| <inline=NoInline> {
|
||||
this@R|special/anonymous|.R|/_|<R|M5Sub|>()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final class C : R|B| {
|
||||
public constructor(): R|C| {
|
||||
super<R|B|>()
|
||||
}
|
||||
|
||||
public final val fromC: R|kotlin/Any| = R|kotlin/Any.Any|()
|
||||
public get(): R|kotlin/Any|
|
||||
|
||||
protected open override fun foo(a1: R|Derived|, a2: R|Derived|): R|M3Sub| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
protected open override fun baz(a1: R|Derived|, a2: R|Derived|): R|M5SubSub| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
public final fun baz(a1: R|Derived|, a2: R|Base|): R|M6| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// SKIP_TXT
|
||||
// FIR_DUMP
|
||||
// CHECK_TYPE
|
||||
|
||||
open class Base
|
||||
class Derived : Base()
|
||||
|
||||
interface M1
|
||||
interface M2
|
||||
interface M2Sub : M2
|
||||
interface M3
|
||||
interface M3Sub : M3
|
||||
interface M4
|
||||
|
||||
|
||||
interface M5
|
||||
interface M5Sub : M5
|
||||
interface M5SubSub : M5Sub
|
||||
interface M6
|
||||
|
||||
open class A {
|
||||
public fun foo(a1: Base, a2: Base): M1 = TODO()
|
||||
protected open fun foo(a1: Base, a2: Derived): M2 = TODO()
|
||||
protected open fun foo(a1: Derived, a2: Derived): M3 = TODO()
|
||||
|
||||
public fun baz(a1: Base, a2: Base): M4 = TODO()
|
||||
protected open fun baz(a1: Derived, a2: Derived): M5 = TODO()
|
||||
}
|
||||
|
||||
open class B : A() {
|
||||
public val fromB: Any = Any()
|
||||
|
||||
override fun foo(a1: Base, a2: Derived): M2Sub = TODO()
|
||||
|
||||
override fun baz(a1: Derived, a2: Derived): M5Sub = TODO()
|
||||
|
||||
fun f(a: A, b: B, c: C, d: Derived) {
|
||||
// Only visible is M1
|
||||
a.foo(d, d) checkType { _<M1>() }
|
||||
// We may call M3 and it's the most specific by its parameters
|
||||
b.foo(d, d) checkType { _<M3>() }
|
||||
// We can't call M3 (and M3Sub) as it's protected overriden in C, so it's invisible because
|
||||
// we only allow to call protected members on dispatch receiver values that are subtypes of a dispatch receiver parameter
|
||||
// So, the next visible and specific member is M2Sub
|
||||
c.foo(d, d) checkType { _<M2Sub>() }
|
||||
|
||||
// Only visible is M4
|
||||
a.baz(d, d) checkType { _<M4>() }
|
||||
// M5Sub is more specific and visible for `b` receiver
|
||||
b.baz(d, d) checkType { _<M5Sub>() }
|
||||
|
||||
// M5SubSub is invisible because it's protected in something that is not our super-class
|
||||
// M6 and M1 are visible, but M6 is more specific
|
||||
c.baz(d, d) checkType { _<M6>() }
|
||||
|
||||
when (a) {
|
||||
is C -> {
|
||||
// Make sure smart cast works
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.fromC
|
||||
// The same logic as for `c.foo`
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.foo(d, d) checkType { _<M2Sub>() }
|
||||
|
||||
// The same logic as for `c.baz`
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.baz(d, d) checkType { _<M6>() }
|
||||
}
|
||||
is B -> {
|
||||
// Make sure smart cast works
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.fromB
|
||||
// The same logic as `b.foo`
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.foo(d, d) checkType { _<M3>() }
|
||||
|
||||
// The same logic as for `b.baz`
|
||||
<!DEBUG_INFO_SMARTCAST!>a<!>.baz(d, d) checkType { _<M5Sub>() }
|
||||
}
|
||||
}
|
||||
|
||||
when (b) {
|
||||
is C -> {
|
||||
<!DEBUG_INFO_SMARTCAST!>b<!>.fromC
|
||||
// In K1, it works just as `c.foo`
|
||||
b.foo(d, d) checkType { _<M2Sub>() }
|
||||
// In K2, M3Sub is invisible, but we have candidate M3 from original receiver and we choose it
|
||||
// Unlike the case of `c.foo` when we choose M2Sub because we don't have more special M3 there in the scope of C
|
||||
// (in the meaning of overload comparison by the value parameter types)
|
||||
b.foo(d, d) checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><M3>() }
|
||||
|
||||
// In K1, it works just as `c.foo`
|
||||
<!DEBUG_INFO_SMARTCAST!>b<!>.baz(d, d) checkType { _<M6>() }
|
||||
// In K2, M5SubSub is invisible, but we have candidate M5Sub from original receiver and we choose it
|
||||
// Unlike the case of `c.baz` when we choose M6 because we don't have more special M5Sub there in the scope of C
|
||||
// (in the meaning of overload comparison by the value parameter types)
|
||||
<!DEBUG_INFO_SMARTCAST!>b<!>.baz(d, d) checkType { <!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>_<!><M5Sub>() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class C : B() {
|
||||
public val fromC: Any = Any()
|
||||
|
||||
override fun foo(a1: Derived, a2: Derived): M3Sub = TODO()
|
||||
|
||||
override fun baz(a1: Derived, a2: Derived): M5SubSub = TODO()
|
||||
// public fallback
|
||||
public fun baz(a1: Derived, a2: Base): M6 = TODO()
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// 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)`)
|
||||
a.baz(d).<!UNRESOLVED_REFERENCE!>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
|
||||
a.baz(d).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()
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
FILE: moreSpecificProtectedSimple.fir.kt
|
||||
public abstract interface Base : R|kotlin/Any| {
|
||||
}
|
||||
public abstract interface Derived : R|Base| {
|
||||
}
|
||||
public abstract interface M1 : R|kotlin/Any| {
|
||||
public abstract val success: R|kotlin/Boolean|
|
||||
public get(): R|kotlin/Boolean|
|
||||
|
||||
}
|
||||
public abstract interface M1Sub : R|M1| {
|
||||
}
|
||||
public open class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
protected open fun baz(a: R|Derived|): R|M1| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
protected open fun foo(a: R|Derived|): R|M1| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
public final fun f(a: R|A|, b: R|B|, d: R|Derived|): R|kotlin/Unit| {
|
||||
R|<local>/a|.R|/A.baz|(R|<local>/d|).R|/M1.success|
|
||||
R|<local>/a|.R|/A.foo|(R|<local>/d|).R|/M1.success|
|
||||
R|<local>/b|.R|/B.baz|(R|<local>/d|).R|kotlin/String.length|
|
||||
R|<local>/b|.R|/B.foo|(R|<local>/d|).R|kotlin/String.length|
|
||||
when (R|<local>/a|) {
|
||||
($subj$ is R|B|) -> {
|
||||
R|<local>/a|.R|/A.baz|(R|<local>/d|).<Unresolved name: length>#
|
||||
R|<local>/a|.R|/A.baz|(R|<local>/d|).R|/M1.success|
|
||||
R|<local>/a|.R|/A.foo|(R|<local>/d|).R|/M1.success|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final class B : R|A| {
|
||||
public constructor(): R|B| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
protected open override fun baz(a: R|Derived|): R|M1Sub| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
public final fun baz(a: R|Base|): R|kotlin/String| {
|
||||
^baz R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
protected open override fun foo(a: R|Derived|): R|M1| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
public final fun foo(a: R|Base|): R|kotlin/String| {
|
||||
^foo R|kotlin/TODO|()
|
||||
}
|
||||
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// 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()
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// SKIP_TXT
|
||||
// ISSUE: KT-55024
|
||||
// MODULE: a
|
||||
interface A {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
internal sealed class B(val x: A) : A {
|
||||
override fun foo() {}
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
// MODULE: b(a)
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
private fun test_1(x: A) {
|
||||
if (x is B) {
|
||||
x.foo()
|
||||
x.bar()
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// SKIP_TXT
|
||||
// ISSUE: KT-55024
|
||||
// MODULE: a
|
||||
interface A {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
internal sealed class B(val x: A) : A {
|
||||
override fun foo() {}
|
||||
fun bar() {}
|
||||
}
|
||||
|
||||
// MODULE: b(a)
|
||||
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||
private fun test_1(x: A) {
|
||||
if (x is B) {
|
||||
x.foo()
|
||||
<!DEBUG_INFO_SMARTCAST!>x<!>.bar()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user