Add test for KT-52782
It just demonstrates/fixate the current "broken" behavior in K1 that we unfortunately have and that is fixed in K2
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
// SKIP_TXT
|
||||
// FILE: Foo.java
|
||||
public abstract class Foo<K extends Bar<? extends Foo<K>>> {
|
||||
abstract String getTest();
|
||||
}
|
||||
|
||||
// FILE: Bar.java
|
||||
public abstract class Bar<T extends Foo<? extends Bar<T>>> {}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun <F : Bar<out Foo<F>>> Foo<F>.bar() {}
|
||||
|
||||
fun box(foo: Foo<*>) {
|
||||
// How inference for the `foo.bar()` call works
|
||||
//
|
||||
// X = Captured(*)
|
||||
// Foo<X> <: Foo<Fv> => X = Fv => NewBound(X <: Fv), NewBound(Fv <: X)
|
||||
//
|
||||
// Incorporation 1:
|
||||
// X <: Fv && Fv <: Bar<out Foo<Fv>> => X <: Bar<out Foo<Fv>>
|
||||
// X has one supertype – Bar<out Foo<X>> ~ Bar<Y> where Y = Captured(out Foo<X>)
|
||||
// X <: Bar<out Foo<Fv>> => Bar<Y> <: Bar<out Foo<Fv>> =>
|
||||
// Y <: Foo<Fv>
|
||||
// Y has two supertypes --> (Foo<out Bar<Y>>, Foo<X>)
|
||||
// Let Z be a captured type in Foo<out Bar<Y>>
|
||||
// Foo<Z> <: Foo<Fv> => Z = Fv =>
|
||||
// But since Z is captured from subtyping we run approximation over it resulting to
|
||||
// Z ~ Bar<out Foo<*>>
|
||||
// NewBound(Bar<out Foo<*>> <: Fv)
|
||||
//
|
||||
// But since Fv <: X, Bar<out Foo<*>> should be a subtype of X, but it is not :(
|
||||
//
|
||||
// Note the problematic part is when Y has two supertypes
|
||||
// And we just check only first one one the second one would work
|
||||
// In K2, it's covered by fork-points, see https://youtrack.jetbrains.com/issue/KT-50401 and org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector.TypeCheckerStateForConstraintInjector.runForkingPoint
|
||||
|
||||
|
||||
// Without approximation (just theoretically what would happen if we didn't approximated FROM_SUBTYPING captured types).
|
||||
// Actually, it's not really relevant but just in case someone need it.
|
||||
//
|
||||
// X = Captured(*)
|
||||
// Foo<X> <: Foo<Fv> => X = Fv => NewBound(X <: Fv), NewBound(Fv <: X)
|
||||
//
|
||||
// Incorporation 1:
|
||||
// X <: Fv && Fv <: Bar<out Foo<Fv>> => X <: Bar<out Foo<Fv>>
|
||||
// X has one supertype – Bar<out Foo<X>> ~ Bar<Y> where Y = Captured(out Foo<X>)
|
||||
// X <: Bar<out Foo<Fv>> => Bar<Y> <: Bar<out Foo<Fv>> =>
|
||||
// Y <: Foo<Fv>
|
||||
// Y has two supertypes --> (Foo<out Bar<Y>>, Foo<X>)
|
||||
// Let Z be a captured type in Foo<out Bar<Y>>
|
||||
// Foo<Z> <: Foo<Fv> => Z = Fv => NewBound(Z <: Fv)
|
||||
//
|
||||
// Incorporation 2:
|
||||
// Z <: Fv && Fv <: Bar<out Foo<Fv>> => Z <: Bar<out Foo<Fv>>
|
||||
// Z has two supertypes --> (Bar<out Foo<Z>>, Bar<Y>)
|
||||
// ... went into recursion
|
||||
|
||||
foo.bar()
|
||||
|
||||
foo.test
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// SKIP_TXT
|
||||
// FILE: Foo.java
|
||||
public abstract class Foo<K extends Bar<? extends Foo<K>>> {
|
||||
abstract String getTest();
|
||||
}
|
||||
|
||||
// FILE: Bar.java
|
||||
public abstract class Bar<T extends Foo<? extends Bar<T>>> {}
|
||||
|
||||
// FILE: main.kt
|
||||
|
||||
fun <F : Bar<out Foo<F>>> Foo<F>.bar() {}
|
||||
|
||||
fun box(foo: Foo<*>) {
|
||||
// How inference for the `foo.bar()` call works
|
||||
//
|
||||
// X = Captured(*)
|
||||
// Foo<X> <: Foo<Fv> => X = Fv => NewBound(X <: Fv), NewBound(Fv <: X)
|
||||
//
|
||||
// Incorporation 1:
|
||||
// X <: Fv && Fv <: Bar<out Foo<Fv>> => X <: Bar<out Foo<Fv>>
|
||||
// X has one supertype – Bar<out Foo<X>> ~ Bar<Y> where Y = Captured(out Foo<X>)
|
||||
// X <: Bar<out Foo<Fv>> => Bar<Y> <: Bar<out Foo<Fv>> =>
|
||||
// Y <: Foo<Fv>
|
||||
// Y has two supertypes --> (Foo<out Bar<Y>>, Foo<X>)
|
||||
// Let Z be a captured type in Foo<out Bar<Y>>
|
||||
// Foo<Z> <: Foo<Fv> => Z = Fv =>
|
||||
// But since Z is captured from subtyping we run approximation over it resulting to
|
||||
// Z ~ Bar<out Foo<*>>
|
||||
// NewBound(Bar<out Foo<*>> <: Fv)
|
||||
//
|
||||
// But since Fv <: X, Bar<out Foo<*>> should be a subtype of X, but it is not :(
|
||||
//
|
||||
// Note the problematic part is when Y has two supertypes
|
||||
// And we just check only first one one the second one would work
|
||||
// In K2, it's covered by fork-points, see https://youtrack.jetbrains.com/issue/KT-50401 and org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector.TypeCheckerStateForConstraintInjector.runForkingPoint
|
||||
|
||||
|
||||
// Without approximation (just theoretically what would happen if we didn't approximated FROM_SUBTYPING captured types).
|
||||
// Actually, it's not really relevant but just in case someone need it.
|
||||
//
|
||||
// X = Captured(*)
|
||||
// Foo<X> <: Foo<Fv> => X = Fv => NewBound(X <: Fv), NewBound(Fv <: X)
|
||||
//
|
||||
// Incorporation 1:
|
||||
// X <: Fv && Fv <: Bar<out Foo<Fv>> => X <: Bar<out Foo<Fv>>
|
||||
// X has one supertype – Bar<out Foo<X>> ~ Bar<Y> where Y = Captured(out Foo<X>)
|
||||
// X <: Bar<out Foo<Fv>> => Bar<Y> <: Bar<out Foo<Fv>> =>
|
||||
// Y <: Foo<Fv>
|
||||
// Y has two supertypes --> (Foo<out Bar<Y>>, Foo<X>)
|
||||
// Let Z be a captured type in Foo<out Bar<Y>>
|
||||
// Foo<Z> <: Foo<Fv> => Z = Fv => NewBound(Z <: Fv)
|
||||
//
|
||||
// Incorporation 2:
|
||||
// Z <: Fv && Fv <: Bar<out Foo<Fv>> => Z <: Bar<out Foo<Fv>>
|
||||
// Z has two supertypes --> (Bar<out Foo<Z>>, Bar<Y>)
|
||||
// ... went into recursion
|
||||
|
||||
foo.<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>bar<!>()
|
||||
|
||||
foo.<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>test<!>
|
||||
}
|
||||
@@ -30,6 +30,8 @@ fun String.asFsdAddress(): String {
|
||||
fun box(): String {
|
||||
val state = Test().state
|
||||
if (state is GoBuildingRunningState<*>) {
|
||||
// Actually, that code is valid and met at IJ, but unfortunately it was broken since 1.7.0
|
||||
// See KT-52782 and testData/diagnostics/tests/inference/capturedTypes/kt52782.kt
|
||||
state.buildingWorkingDirectory.asFsdAddress()
|
||||
}
|
||||
return "OK"
|
||||
|
||||
@@ -30,6 +30,8 @@ fun String.asFsdAddress(): String {
|
||||
fun box(): String {
|
||||
val state = Test().state
|
||||
if (state is GoBuildingRunningState<*>) {
|
||||
// Actually, that code is valid and met at IJ, but unfortunately it was broken since 1.7.0
|
||||
// See KT-52782 and testData/diagnostics/tests/inference/capturedTypes/kt52782.kt
|
||||
state.<!UNRESOLVED_REFERENCE_WRONG_RECEIVER!>buildingWorkingDirectory<!>.<!DEBUG_INFO_MISSING_UNRESOLVED!>asFsdAddress<!>()
|
||||
}
|
||||
return "OK"
|
||||
|
||||
Reference in New Issue
Block a user