[IR] Consider flexibility of the return type for intersection overrides

^KT-65207 Fixed
This commit is contained in:
Dmitriy Novozhilov
2024-02-16 09:38:26 +02:00
committed by Space Team
parent 9302c8c14d
commit 3c86a4f9df
4 changed files with 74 additions and 74 deletions
@@ -19,7 +19,6 @@ import org.jetbrains.kotlin.ir.util.isClass
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.utils.filterIsInstanceAnd import org.jetbrains.kotlin.utils.filterIsInstanceAnd
import org.jetbrains.kotlin.utils.memoryOptimizedMap import org.jetbrains.kotlin.utils.memoryOptimizedMap
import org.jetbrains.kotlin.utils.memoryOptimizedMapNotNull import org.jetbrains.kotlin.utils.memoryOptimizedMapNotNull
@@ -233,7 +232,6 @@ class IrFakeOverrideBuilder(
return member ?: error("Could not find a visible member") return member ?: error("Could not find a visible member")
} }
private fun createAndBindFakeOverrides( private fun createAndBindFakeOverrides(
current: IrClass, current: IrClass,
notOverridden: Collection<FakeOverride>, notOverridden: Collection<FakeOverride>,
@@ -242,9 +240,9 @@ class IrFakeOverrideBuilder(
) { ) {
val fromSuper = notOverridden.toMutableSet() val fromSuper = notOverridden.toMutableSet()
while (fromSuper.isNotEmpty()) { while (fromSuper.isNotEmpty()) {
val notOverriddenFromSuper = findMemberWithMaxVisibility(filterOutCustomizedFakeOverrides(fromSuper)) val notOverriddenFromSuper = filterOutCustomizedFakeOverrides(fromSuper)
val overridables = extractMembersOverridableInBothWays( val overridables = extractMembersOverridableInBothWays(
notOverriddenFromSuper, notOverriddenFromSuper.first(),
fromSuper fromSuper
) )
createAndBindFakeOverride(overridables, current, addedFakeOverrides, compatibilityMode) createAndBindFakeOverride(overridables, current, addedFakeOverrides, compatibilityMode)
@@ -367,73 +365,77 @@ class IrFakeOverrideBuilder(
strategy.postProcessGeneratedFakeOverride(fakeOverride, currentClass) strategy.postProcessGeneratedFakeOverride(fakeOverride, currentClass)
} }
private fun isVisibilityMoreSpecific( private fun isReturnTypeIsSubtypeOfOtherReturnType(
a: IrOverridableMember, a: IrOverridableMember,
b: IrOverridableMember
): Boolean {
val result =
DescriptorVisibilities.compare(a.visibility, b.visibility)
return result == null || result >= 0
}
private fun isAccessorMoreSpecific(
a: IrSimpleFunction?,
b: IrSimpleFunction?
): Boolean {
return if (a == null || b == null) true else isVisibilityMoreSpecific(a, b)
}
private fun TypeCheckerState.isSubtypeOf(a: IrType, b: IrType) =
AbstractTypeChecker.isSubtypeOf(this, a, b)
private fun TypeCheckerState.equalTypes(a: IrType, b: IrType) =
AbstractTypeChecker.equalTypes(this, a, b)
private fun createTypeCheckerState(a: List<IrTypeParameter>, b: List<IrTypeParameter>): TypeCheckerState =
createIrTypeCheckerState(IrTypeSystemContextWithAdditionalAxioms(typeSystem, a, b))
private fun isReturnTypeMoreSpecific(
a: IrOverridableMember,
aReturnType: IrType,
b: IrOverridableMember, b: IrOverridableMember,
bReturnType: IrType
): Boolean { ): Boolean {
val typeCheckerState = createTypeCheckerState(a.typeParameters, b.typeParameters) val typeCheckerState = createIrTypeCheckerState(
return typeCheckerState.isSubtypeOf(aReturnType, bReturnType) IrTypeSystemContextWithAdditionalAxioms(typeSystem, a.typeParameters, b.typeParameters)
)
return AbstractTypeChecker.isSubtypeOf(typeCheckerState, a.returnType, b.returnType)
} }
private fun isMoreSpecific( private fun isMoreSpecific(
a: IrOverridableMember, a: IrOverridableMember,
b: IrOverridableMember b: IrOverridableMember
): Boolean { ): Boolean {
val aReturnType = a.returnType return a > b
val bReturnType = b.returnType }
if (!isVisibilityMoreSpecific(a, b)) return false
if (a is IrSimpleFunction) { // Based on compareTo from FirOverrideService.kt
require(b is IrSimpleFunction) { "b is " + b.javaClass } private operator fun IrOverridableMember.compareTo(other: IrOverridableMember): Int {
return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType) fun merge(preferA: Boolean, preferB: Boolean, previous: Int): Int = when {
preferA == preferB -> previous
preferA && previous >= 0 -> 1
preferB && previous <= 0 -> -1
else -> 0
} }
if (a is IrProperty) {
require(b is IrProperty) { "b is " + b.javaClass } val aIr = this@compareTo
if (!isAccessorMoreSpecific( val bIr = other
a.setter, val byVisibility = DescriptorVisibilities.compare(aIr.visibility, bIr.visibility) ?: 0
b.setter val aReturnType = aIr.returnType
) val bReturnType = bIr.returnType
) return false
return if (a.isVar && b.isVar) { val aSubtypesB = isReturnTypeIsSubtypeOfOtherReturnType(aIr, bIr)
createTypeCheckerState( val bSubtypesA = isReturnTypeIsSubtypeOfOtherReturnType(bIr, aIr)
a.getter!!.typeParameters,
b.getter!!.typeParameters val byVisibilityAndType = when {
).equalTypes(aReturnType, bReturnType) // Could be that one of them is flexible, in which case the types are not equal but still subtypes of one another;
} else { // make the inflexible one more specific.
// both vals or var vs val: val can't be more specific then var aSubtypesB && bSubtypesA -> merge(!aReturnType.isFlexible(), !bReturnType.isFlexible(), byVisibility)
!(!a.isVar && b.isVar) && isReturnTypeMoreSpecific( aSubtypesB && byVisibility >= 0 -> 1
a, aReturnType, bSubtypesA && byVisibility <= 0 -> -1
b, bReturnType else -> 0
}
return when (aIr) {
is IrSimpleFunction -> byVisibilityAndType
is IrProperty -> {
require(bIr is IrProperty)
val settersComparison = if (aIr.isVar && bIr.isVar) {
val aSetter = aIr.setter
val bSetter = bIr.setter
when {
aSetter != null && bSetter != null -> aSetter.compareTo(bSetter)
else -> 0
}
} else {
0
}
val bySetters = merge(
preferA = settersComparison >= 0,
preferB = settersComparison <= 0,
byVisibilityAndType
) )
merge(aIr.isVar, bIr.isVar, bySetters)
} }
else -> error("Unexpected type: $aIr")
} }
error("Unexpected callable: $a") }
private fun IrType.isFlexible(): Boolean {
return with(typeSystem) { isFlexible() }
} }
private fun isMoreSpecificThenAllOf( private fun isMoreSpecificThenAllOf(
@@ -450,9 +452,7 @@ class IrFakeOverrideBuilder(
return true return true
} }
private fun selectMostSpecificMember( private fun selectMostSpecificMember(overridables: Collection<FakeOverride>): FakeOverride {
overridables: Collection<FakeOverride>
): FakeOverride {
require(!overridables.isEmpty()) { "Should have at least one overridable member" } require(!overridables.isEmpty()) { "Should have at least one overridable member" }
if (overridables.size == 1) { if (overridables.size == 1) {
return overridables.first() return overridables.first()
@@ -13,4 +13,4 @@ abstract class J {
abstract fun foo(): Child abstract fun foo(): Child
} }
abstract class A : I, J() abstract class A : I, J()
@@ -7,22 +7,22 @@ FILE fqName:<root> fileName:/1.kt
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.Java1' DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in <root>.Java1'
<T>: R of <root>.C <T>: R of <root>.C
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:C modality:ABSTRACT visibility:public superTypes:[<root>.Java1<R of <root>.C>; <root>.KotlinInterface<R of <root>.C>]' INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:C modality:ABSTRACT visibility:public superTypes:[<root>.Java1<R of <root>.C>; <root>.KotlinInterface<R of <root>.C>]'
FUN FAKE_OVERRIDE name:bar visibility:public modality:OPEN <> ($this:<root>.Java1<R of <root>.C>) returnType:@[FlexibleNullability] java.util.ArrayList<@[FlexibleNullability] kotlin.Int?>? [fake_override] FUN FAKE_OVERRIDE name:bar visibility:public modality:OPEN <> ($this:<root>.KotlinInterface<R of <root>.C>) returnType:java.util.ArrayList<kotlin.Int> [fake_override]
overridden: overridden:
public open fun bar (): @[FlexibleNullability] java.util.ArrayList<@[FlexibleNullability] kotlin.Int?>? declared in <root>.Java1 public open fun bar (): @[FlexibleNullability] java.util.ArrayList<@[FlexibleNullability] kotlin.Int?>? declared in <root>.Java1
public abstract fun bar (): java.util.ArrayList<kotlin.Int> declared in <root>.KotlinInterface public abstract fun bar (): java.util.ArrayList<kotlin.Int> declared in <root>.KotlinInterface
$this: VALUE_PARAMETER name:<this> type:<root>.Java1<R of <root>.C> $this: VALUE_PARAMETER name:<this> type:<root>.KotlinInterface<R of <root>.C>
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden: overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java1 public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.Java1
public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.KotlinInterface public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in <root>.KotlinInterface
$this: VALUE_PARAMETER name:<this> type:kotlin.Any $this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any? VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:foo visibility:public modality:OPEN <> ($this:<root>.Java1<R of <root>.C>) returnType:@[FlexibleNullability] R of <root>.C? [fake_override] FUN FAKE_OVERRIDE name:foo visibility:public modality:OPEN <> ($this:<root>.KotlinInterface<R of <root>.C>) returnType:R of <root>.C [fake_override]
overridden: overridden:
public open fun foo (): @[FlexibleNullability] T of <root>.Java1? declared in <root>.Java1 public open fun foo (): @[FlexibleNullability] T of <root>.Java1? declared in <root>.Java1
public abstract fun foo (): T of <root>.KotlinInterface declared in <root>.KotlinInterface public abstract fun foo (): T of <root>.KotlinInterface declared in <root>.KotlinInterface
$this: VALUE_PARAMETER name:<this> type:<root>.Java1<R of <root>.C> $this: VALUE_PARAMETER name:<this> type:<root>.KotlinInterface<R of <root>.C>
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden: overridden:
public open fun hashCode (): kotlin.Int declared in <root>.Java1 public open fun hashCode (): kotlin.Int declared in <root>.Java1
@@ -9,16 +9,16 @@ abstract class C<R : Any?> : Java1<R>, KotlinInterface<R> {
constructor() /* primary */ constructor() /* primary */
// CHECK JVM_IR: // CHECK JVM_IR:
// Mangled name: C#bar(){}java.util.ArrayList<kotlin.Int?>? // Mangled name: C#bar(){}java.util.ArrayList<kotlin.Int>
// Public signature: /C.bar|6344786471503787921[0] // Public signature: /C.bar|3123552224271008170[0]
// Public signature debug description: bar(){}java.util.ArrayList<kotlin.Int?>? // Public signature debug description: bar(){}java.util.ArrayList<kotlin.Int>
/* fake */ override fun bar(): ArrayList<Int?>? /* fake */ override fun bar(): ArrayList<Int>
// CHECK JVM_IR: // CHECK JVM_IR:
// Mangled name: C#foo(){}1:0? // Mangled name: C#foo(){}1:0
// Public signature: /C.foo|-1179576840814333363[0] // Public signature: /C.foo|-5478580208809996038[0]
// Public signature debug description: foo(){}1:0? // Public signature debug description: foo(){}1:0
/* fake */ override fun foo(): R? /* fake */ override fun foo(): R
} }