From 3c86a4f9df23b57fd1a143db870d206521168089 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 16 Feb 2024 09:38:26 +0200 Subject: [PATCH] [IR] Consider flexibility of the return type for intersection overrides ^KT-65207 Fixed --- .../ir/overrides/IrFakeOverrideBuilder.kt | 122 +++++++++--------- .../declarations/fakeOverrideModality.kt | 2 +- .../intersectionWithJava.fir.ir.txt | 8 +- .../intersectionWithJava.sig.kt.txt | 16 +-- 4 files changed, 74 insertions(+), 74 deletions(-) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt index 29a95f405df..5ecd7d4d84b 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrFakeOverrideBuilder.kt @@ -19,7 +19,6 @@ import org.jetbrains.kotlin.ir.util.isClass import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo import org.jetbrains.kotlin.types.AbstractTypeChecker -import org.jetbrains.kotlin.types.TypeCheckerState import org.jetbrains.kotlin.utils.filterIsInstanceAnd import org.jetbrains.kotlin.utils.memoryOptimizedMap import org.jetbrains.kotlin.utils.memoryOptimizedMapNotNull @@ -233,7 +232,6 @@ class IrFakeOverrideBuilder( return member ?: error("Could not find a visible member") } - private fun createAndBindFakeOverrides( current: IrClass, notOverridden: Collection, @@ -242,9 +240,9 @@ class IrFakeOverrideBuilder( ) { val fromSuper = notOverridden.toMutableSet() while (fromSuper.isNotEmpty()) { - val notOverriddenFromSuper = findMemberWithMaxVisibility(filterOutCustomizedFakeOverrides(fromSuper)) + val notOverriddenFromSuper = filterOutCustomizedFakeOverrides(fromSuper) val overridables = extractMembersOverridableInBothWays( - notOverriddenFromSuper, + notOverriddenFromSuper.first(), fromSuper ) createAndBindFakeOverride(overridables, current, addedFakeOverrides, compatibilityMode) @@ -367,73 +365,77 @@ class IrFakeOverrideBuilder( strategy.postProcessGeneratedFakeOverride(fakeOverride, currentClass) } - private fun isVisibilityMoreSpecific( + private fun isReturnTypeIsSubtypeOfOtherReturnType( 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, b: List): TypeCheckerState = - createIrTypeCheckerState(IrTypeSystemContextWithAdditionalAxioms(typeSystem, a, b)) - - private fun isReturnTypeMoreSpecific( - a: IrOverridableMember, - aReturnType: IrType, b: IrOverridableMember, - bReturnType: IrType ): Boolean { - val typeCheckerState = createTypeCheckerState(a.typeParameters, b.typeParameters) - return typeCheckerState.isSubtypeOf(aReturnType, bReturnType) + val typeCheckerState = createIrTypeCheckerState( + IrTypeSystemContextWithAdditionalAxioms(typeSystem, a.typeParameters, b.typeParameters) + ) + return AbstractTypeChecker.isSubtypeOf(typeCheckerState, a.returnType, b.returnType) } private fun isMoreSpecific( a: IrOverridableMember, b: IrOverridableMember ): Boolean { - val aReturnType = a.returnType - val bReturnType = b.returnType - if (!isVisibilityMoreSpecific(a, b)) return false - if (a is IrSimpleFunction) { - require(b is IrSimpleFunction) { "b is " + b.javaClass } - return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType) + return a > b + } + + // Based on compareTo from FirOverrideService.kt + private operator fun IrOverridableMember.compareTo(other: IrOverridableMember): Int { + 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 } - if (!isAccessorMoreSpecific( - a.setter, - b.setter - ) - ) return false - return if (a.isVar && b.isVar) { - createTypeCheckerState( - a.getter!!.typeParameters, - b.getter!!.typeParameters - ).equalTypes(aReturnType, bReturnType) - } else { - // both vals or var vs val: val can't be more specific then var - !(!a.isVar && b.isVar) && isReturnTypeMoreSpecific( - a, aReturnType, - b, bReturnType + + val aIr = this@compareTo + val bIr = other + val byVisibility = DescriptorVisibilities.compare(aIr.visibility, bIr.visibility) ?: 0 + val aReturnType = aIr.returnType + val bReturnType = bIr.returnType + + val aSubtypesB = isReturnTypeIsSubtypeOfOtherReturnType(aIr, bIr) + val bSubtypesA = isReturnTypeIsSubtypeOfOtherReturnType(bIr, aIr) + + val byVisibilityAndType = when { + // Could be that one of them is flexible, in which case the types are not equal but still subtypes of one another; + // make the inflexible one more specific. + aSubtypesB && bSubtypesA -> merge(!aReturnType.isFlexible(), !bReturnType.isFlexible(), byVisibility) + aSubtypesB && byVisibility >= 0 -> 1 + bSubtypesA && byVisibility <= 0 -> -1 + 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( @@ -450,9 +452,7 @@ class IrFakeOverrideBuilder( return true } - private fun selectMostSpecificMember( - overridables: Collection - ): FakeOverride { + private fun selectMostSpecificMember(overridables: Collection): FakeOverride { require(!overridables.isEmpty()) { "Should have at least one overridable member" } if (overridables.size == 1) { return overridables.first() diff --git a/compiler/testData/ir/irText/declarations/fakeOverrideModality.kt b/compiler/testData/ir/irText/declarations/fakeOverrideModality.kt index 79203d222ce..cc0cdf2d43d 100644 --- a/compiler/testData/ir/irText/declarations/fakeOverrideModality.kt +++ b/compiler/testData/ir/irText/declarations/fakeOverrideModality.kt @@ -13,4 +13,4 @@ abstract class J { abstract fun foo(): Child } -abstract class A : I, J() \ No newline at end of file +abstract class A : I, J() diff --git a/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.fir.ir.txt b/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.fir.ir.txt index c3caabf2607..a013462e42c 100644 --- a/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.fir.ir.txt +++ b/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.fir.ir.txt @@ -7,22 +7,22 @@ FILE fqName: fileName:/1.kt DELEGATING_CONSTRUCTOR_CALL 'public constructor () declared in .Java1' : R of .C INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:C modality:ABSTRACT visibility:public superTypes:[.Java1.C>; .KotlinInterface.C>]' - FUN FAKE_OVERRIDE name:bar visibility:public modality:OPEN <> ($this:.Java1.C>) returnType:@[FlexibleNullability] java.util.ArrayList<@[FlexibleNullability] kotlin.Int?>? [fake_override] + FUN FAKE_OVERRIDE name:bar visibility:public modality:OPEN <> ($this:.KotlinInterface.C>) returnType:java.util.ArrayList [fake_override] overridden: public open fun bar (): @[FlexibleNullability] java.util.ArrayList<@[FlexibleNullability] kotlin.Int?>? declared in .Java1 public abstract fun bar (): java.util.ArrayList declared in .KotlinInterface - $this: VALUE_PARAMETER name: type:.Java1.C> + $this: VALUE_PARAMETER name: type:.KotlinInterface.C> FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] overridden: public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in .Java1 public open fun equals (other: kotlin.Any?): kotlin.Boolean declared in .KotlinInterface $this: VALUE_PARAMETER name: type:kotlin.Any VALUE_PARAMETER name:other index:0 type:kotlin.Any? - FUN FAKE_OVERRIDE name:foo visibility:public modality:OPEN <> ($this:.Java1.C>) returnType:@[FlexibleNullability] R of .C? [fake_override] + FUN FAKE_OVERRIDE name:foo visibility:public modality:OPEN <> ($this:.KotlinInterface.C>) returnType:R of .C [fake_override] overridden: public open fun foo (): @[FlexibleNullability] T of .Java1? declared in .Java1 public abstract fun foo (): T of .KotlinInterface declared in .KotlinInterface - $this: VALUE_PARAMETER name: type:.Java1.C> + $this: VALUE_PARAMETER name: type:.KotlinInterface.C> FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] overridden: public open fun hashCode (): kotlin.Int declared in .Java1 diff --git a/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.sig.kt.txt b/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.sig.kt.txt index b9eeddd8e7e..2b8001001a6 100644 --- a/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.sig.kt.txt +++ b/compiler/testData/ir/irText/fakeOverrides/intersectionWithJava.sig.kt.txt @@ -9,16 +9,16 @@ abstract class C : Java1, KotlinInterface { constructor() /* primary */ // CHECK JVM_IR: - // Mangled name: C#bar(){}java.util.ArrayList? - // Public signature: /C.bar|6344786471503787921[0] - // Public signature debug description: bar(){}java.util.ArrayList? - /* fake */ override fun bar(): ArrayList? + // Mangled name: C#bar(){}java.util.ArrayList + // Public signature: /C.bar|3123552224271008170[0] + // Public signature debug description: bar(){}java.util.ArrayList + /* fake */ override fun bar(): ArrayList // CHECK JVM_IR: - // Mangled name: C#foo(){}1:0? - // Public signature: /C.foo|-1179576840814333363[0] - // Public signature debug description: foo(){}1:0? - /* fake */ override fun foo(): R? + // Mangled name: C#foo(){}1:0 + // Public signature: /C.foo|-5478580208809996038[0] + // Public signature debug description: foo(){}1:0 + /* fake */ override fun foo(): R }