[FIR] Allow declarations to override parts of non-trivial intersection

A class can inherit two declarations that are compatible from the
overridability standpoint and are therefore combined to a non-trivial
intersection.
At the same time, the class can declare a member declaration that
only overrides one of the intersection's members.
In this case, we break up the intersection and only add the overridden
parts to the declared member's direct overridden list.

If the class doesn't override the intersection, it exists as
intersection override, like before.

#KT-65487 Fixed
This commit is contained in:
Kirill Rakhman
2024-02-15 18:00:48 +01:00
committed by Space Team
parent 889182629e
commit 26fae9e83a
34 changed files with 1399 additions and 42 deletions
@@ -0,0 +1,50 @@
// TARGET_BACKEND: JVM
// FILE: Base.java
import org.jetbrains.annotations.NotNull;
public interface Base {
Base foo(@NotNull String name);
}
// FILE: Derived.java
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface Derived extends Base {
@Override
@NotNull Derived foo(@Nullable String name);
}
// FILE: Impl.kt
abstract class Impl : Base {
override fun foo(name: String): Base = this
}
// FILE: test.kt
abstract class ImplDerived : Impl(), Derived {
abstract override fun foo(name: String?): Derived
}
abstract class DerivedImpl : Derived, Impl() {
abstract override fun foo(name: String?): Derived
}
fun box(): String {
val x1: ImplDerived = object : ImplDerived() {
override fun foo(name: String?): Derived = this
}
x1.foo("")
(x1 as Base).foo("")
(x1 as Derived).foo("")
(x1 as Impl).foo("")
val x2: DerivedImpl = object : DerivedImpl() {
override fun foo(name: String?): Derived = this
}
x2.foo("")
(x2 as Base).foo("")
(x2 as Derived).foo("")
(x2 as Impl).foo("")
return "OK"
}
@@ -0,0 +1,86 @@
// TARGET_BACKEND: JVM_IR
// ^Old backend reports CONFLICTING_JVM_DECLARATIONS on C1, K1 with IR is fine.
// FULL_JDK
// FILE: Remove.java
public interface Remove {
Boolean remove(Integer element);
}
// FILE: GetBoxed.java
public interface GetBoxed {
Character get(Integer i);
}
// FILE: GetPrimitive.java
public interface GetPrimitive {
Character get(int i);
}
// FILE: MyString.java
public abstract class MyString implements CharSequence {
@Override public char charAt(int i) { return 'j'; }
@Override public int length() { return 0; }
@Override public CharSequence subSequence(int start, int end) { return null; }
}
// FILE: box.kt
class RemoveOverridden : ArrayList<Int>(), Remove {
// Overrides Remove.remove but not ArrayList.remove
override fun remove(element: Int?): Boolean = false
}
class RemoveNotOverridden : ArrayList<Int>(), Remove {
}
class GetBoxedOverridden : MyString(), GetBoxed {
// Overrides GetBoxed.get but not MyString.charAt
override fun get(i: Int?): Char? = 'k'
}
class GetPrimitiveOverridden : MyString(), GetPrimitive {
// Overrides GetPrimitive.get and MyString.charAt
override fun get(i: Int): Char = 'k'
}
fun box(): String {
val r1 = RemoveOverridden()
r1.add(1)
r1.add(2)
r1.remove(1 as Int?)
(r1 as Remove).remove(1)
if (1 !in r1) return "FAIL 1"
r1.remove(1)
if (1 in r1) return "FAIL 2"
(r1 as ArrayList<Int>).remove(2)
if (2 in r1) return "FAIL 3"
val r2 = RemoveNotOverridden()
r2.add(1)
r2.add(2)
r2.add(3)
(r2 as Remove).remove(1)
if (1 in r2) return "FAIL 4"
r2.remove(2)
if (1 in r2) return "FAIL 5"
(r2 as ArrayList<Int>).remove(3)
if (2 in r2) return "FAIL 6"
val g1 = GetBoxedOverridden()
if (g1.get(1) != 'j') return "FAIL 7"
if ((g1 as MyString).get(1) != 'j') return "FAIL 8"
if ((g1 as GetBoxed).get(1) != 'k') return "FAIL 9"
val g2 = GetPrimitiveOverridden()
if (g2.get(1) != 'k') return "FAIL 10"
if ((g2 as MyString).get(1) != 'k') return "FAIL 11"
if ((g2 as GetPrimitive).get(1) != 'k') return "FAIL 12"
return "OK"
}