[IR Actualizer] Fix fake-override generation in actual A -> common B -> actual C hierarchy
... where C defines a member x and A overrides the member x #KT-61166 Fixed
This commit is contained in:
committed by
Space Team
parent
293474ee50
commit
ac102dedac
+6
@@ -33701,6 +33701,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt61166.kt")
|
||||
public void testKt61166() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noArgActualConstructor.kt")
|
||||
public void testNoArgActualConstructor() throws Exception {
|
||||
|
||||
+6
@@ -33701,6 +33701,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt61166.kt")
|
||||
public void testKt61166() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noArgActualConstructor.kt")
|
||||
public void testNoArgActualConstructor() throws Exception {
|
||||
|
||||
+28
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.overrides.isOverridableByWithoutExternalConditions
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
||||
@@ -20,6 +21,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
|
||||
/**
|
||||
* It adds fake overrides to non-expect classes inside common or multi-platform module,
|
||||
@@ -79,6 +81,32 @@ internal class ActualFakeOverridesAdder(
|
||||
) {
|
||||
for (symbolFromSupertype in membersFromSupertype) {
|
||||
val memberFromSupertype = symbolFromSupertype.owner as IrDeclaration
|
||||
|
||||
if (memberFromSupertype is IrOverridableMember) {
|
||||
// We can land here because of a hierarchy like
|
||||
// actual A -> common B -> actual C
|
||||
// where C defines a member x and A overrides the member x.
|
||||
// We will first add a fake-override x to B and then land here.
|
||||
// In this case we don't want to add a fake-override on top of the real override to A.
|
||||
// Instead, we add the fake-override x to the overridden symbols of A.x.
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val override = klass.declarations.firstOrNull {
|
||||
it is IrOverridableMember &&
|
||||
typeSystemContext.isOverridableByWithoutExternalConditions(
|
||||
superMember = memberFromSupertype,
|
||||
subMember = it,
|
||||
checkIsInlineFlag = false,
|
||||
checkReturnType = false
|
||||
).result == OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
|
||||
} as? IrOverridableDeclaration<IrSymbol>
|
||||
|
||||
if (override != null) {
|
||||
override.overriddenSymbols += symbolFromSupertype
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
val newMember = createFakeOverrideMember(listOf(memberFromSupertype), klass)
|
||||
val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect(
|
||||
newMember.symbol,
|
||||
|
||||
@@ -627,63 +627,64 @@ class IrOverridingUtil(
|
||||
checkIsInlineFlag: Boolean,
|
||||
checkReturnType: Boolean
|
||||
): OverrideCompatibilityInfo {
|
||||
return isOverridableByWithoutExternalConditions(superMember, subMember, checkIsInlineFlag, checkReturnType)
|
||||
return typeSystem.isOverridableByWithoutExternalConditions(superMember, subMember, checkIsInlineFlag, checkReturnType)
|
||||
// The frontend goes into external overridability condition details here, but don't deal with them in IR (yet?).
|
||||
}
|
||||
}
|
||||
|
||||
private fun isOverridableByWithoutExternalConditions(
|
||||
superMember: IrOverridableMember,
|
||||
subMember: IrOverridableMember,
|
||||
checkIsInlineFlag: Boolean,
|
||||
checkReturnType: Boolean
|
||||
): OverrideCompatibilityInfo {
|
||||
val superTypeParameters: List<IrTypeParameter>
|
||||
val subTypeParameters: List<IrTypeParameter>
|
||||
fun IrTypeSystemContext.isOverridableByWithoutExternalConditions(
|
||||
superMember: IrOverridableMember,
|
||||
subMember: IrOverridableMember,
|
||||
checkIsInlineFlag: Boolean,
|
||||
checkReturnType: Boolean
|
||||
): OverrideCompatibilityInfo {
|
||||
val superTypeParameters: List<IrTypeParameter>
|
||||
val subTypeParameters: List<IrTypeParameter>
|
||||
|
||||
val superValueParameters: List<IrValueParameter>
|
||||
val subValueParameters: List<IrValueParameter>
|
||||
val superValueParameters: List<IrValueParameter>
|
||||
val subValueParameters: List<IrValueParameter>
|
||||
|
||||
when (superMember) {
|
||||
is IrSimpleFunction -> when {
|
||||
subMember !is IrSimpleFunction -> return incompatible("Member kind mismatch")
|
||||
superMember.hasExtensionReceiver != subMember.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
||||
superMember.isSuspend != subMember.isSuspend -> return incompatible("Incompatible suspendability")
|
||||
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline function can't be overridden")
|
||||
when (superMember) {
|
||||
is IrSimpleFunction -> when {
|
||||
subMember !is IrSimpleFunction -> return incompatible("Member kind mismatch")
|
||||
superMember.hasExtensionReceiver != subMember.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
||||
superMember.isSuspend != subMember.isSuspend -> return incompatible("Incompatible suspendability")
|
||||
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline function can't be overridden")
|
||||
|
||||
else -> {
|
||||
superTypeParameters = superMember.typeParameters
|
||||
subTypeParameters = subMember.typeParameters
|
||||
superValueParameters = superMember.compiledValueParameters
|
||||
subValueParameters = subMember.compiledValueParameters
|
||||
}
|
||||
else -> {
|
||||
superTypeParameters = superMember.typeParameters
|
||||
subTypeParameters = subMember.typeParameters
|
||||
superValueParameters = superMember.compiledValueParameters
|
||||
subValueParameters = subMember.compiledValueParameters
|
||||
}
|
||||
is IrProperty -> when {
|
||||
subMember !is IrProperty -> return incompatible("Member kind mismatch")
|
||||
superMember.getter.hasExtensionReceiver != subMember.getter.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
||||
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline property can't be overridden")
|
||||
}
|
||||
is IrProperty -> when {
|
||||
subMember !is IrProperty -> return incompatible("Member kind mismatch")
|
||||
superMember.getter.hasExtensionReceiver != subMember.getter.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
||||
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline property can't be overridden")
|
||||
|
||||
else -> {
|
||||
superTypeParameters = superMember.typeParameters
|
||||
subTypeParameters = subMember.typeParameters
|
||||
superValueParameters = superMember.compiledValueParameters
|
||||
subValueParameters = subMember.compiledValueParameters
|
||||
}
|
||||
else -> {
|
||||
superTypeParameters = superMember.typeParameters
|
||||
subTypeParameters = subMember.typeParameters
|
||||
superValueParameters = superMember.compiledValueParameters
|
||||
subValueParameters = subMember.compiledValueParameters
|
||||
}
|
||||
else -> error("Unexpected type of declaration: ${superMember::class.java}, $superMember")
|
||||
}
|
||||
else -> error("Unexpected type of declaration: ${superMember::class.java}, $superMember")
|
||||
}
|
||||
|
||||
when {
|
||||
superMember.name != subMember.name -> {
|
||||
// Check name after member kind checks. This way FO builder will first check types of overridable members and crash
|
||||
// if member types are not supported (ex: IrConstructor).
|
||||
return incompatible("Name mismatch")
|
||||
}
|
||||
|
||||
when {
|
||||
superMember.name != subMember.name -> {
|
||||
// Check name after member kind checks. This way FO builder will first check types of overridable members and crash
|
||||
// if member types are not supported (ex: IrConstructor).
|
||||
return incompatible("Name mismatch")
|
||||
}
|
||||
superTypeParameters.size != subTypeParameters.size -> return incompatible("Type parameter number mismatch")
|
||||
superValueParameters.size != subValueParameters.size -> return incompatible("Value parameter number mismatch")
|
||||
}
|
||||
|
||||
superTypeParameters.size != subTypeParameters.size -> return incompatible("Type parameter number mismatch")
|
||||
superValueParameters.size != subValueParameters.size -> return incompatible("Value parameter number mismatch")
|
||||
}
|
||||
|
||||
// TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
|
||||
// TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
|
||||
// superTypeParameters.forEachIndexed { index, parameter ->
|
||||
// if (!AbstractTypeChecker.areTypeParametersEquivalent(
|
||||
// typeCheckerContext as AbstractTypeCheckerContext,
|
||||
@@ -693,34 +694,33 @@ class IrOverridingUtil(
|
||||
// ) return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch")
|
||||
// }
|
||||
|
||||
val typeCheckerState = createIrTypeCheckerState(
|
||||
IrTypeSystemContextWithAdditionalAxioms(
|
||||
typeSystem,
|
||||
superTypeParameters,
|
||||
subTypeParameters
|
||||
)
|
||||
val typeCheckerState = createIrTypeCheckerState(
|
||||
IrTypeSystemContextWithAdditionalAxioms(
|
||||
this,
|
||||
superTypeParameters,
|
||||
subTypeParameters
|
||||
)
|
||||
)
|
||||
|
||||
superValueParameters.forEachIndexed { index, parameter ->
|
||||
if (!AbstractTypeChecker.equalTypes(
|
||||
typeCheckerState,
|
||||
subValueParameters[index].type,
|
||||
parameter.type
|
||||
)
|
||||
) return incompatible("Value parameter type mismatch")
|
||||
}
|
||||
|
||||
if (checkReturnType) {
|
||||
if (!AbstractTypeChecker.isSubtypeOf(
|
||||
typeCheckerState,
|
||||
subMember.returnType,
|
||||
superMember.returnType
|
||||
)
|
||||
) return conflict("Return type mismatch")
|
||||
}
|
||||
|
||||
return success()
|
||||
superValueParameters.forEachIndexed { index, parameter ->
|
||||
if (!AbstractTypeChecker.equalTypes(
|
||||
typeCheckerState,
|
||||
subValueParameters[index].type,
|
||||
parameter.type
|
||||
)
|
||||
) return incompatible("Value parameter type mismatch")
|
||||
}
|
||||
|
||||
if (checkReturnType) {
|
||||
if (!AbstractTypeChecker.isSubtypeOf(
|
||||
typeCheckerState,
|
||||
subMember.returnType,
|
||||
superMember.returnType
|
||||
)
|
||||
) return conflict("Return type mismatch")
|
||||
}
|
||||
|
||||
return success()
|
||||
}
|
||||
|
||||
private val IrSimpleFunction?.hasExtensionReceiver: Boolean
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// LANGUAGE: +MultiPlatformProjects
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND_K1: ANY
|
||||
// ISSUE: KT-60854
|
||||
// WITH_STDLIB
|
||||
// FULL_JDK
|
||||
|
||||
// MODULE: common
|
||||
// FILE: common.kt
|
||||
expect object A
|
||||
abstract class B : C()
|
||||
expect abstract class C()
|
||||
|
||||
// MODULE: intermediate()()(common)
|
||||
// FILE: intermediate.kt
|
||||
actual object A : B() {
|
||||
override val x: String get() = "OK"
|
||||
}
|
||||
|
||||
actual abstract class C {
|
||||
abstract val x: String
|
||||
}
|
||||
// MODULE: platform()()(intermediate)
|
||||
// FILE: platform.kt
|
||||
|
||||
fun box(): String = A.x
|
||||
+1
-1
@@ -9,7 +9,7 @@ expect open class C1()
|
||||
expect interface I1
|
||||
|
||||
open class A : C1(), I1
|
||||
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED{JVM}!>open class B : I1, C1()<!>
|
||||
open class B : I1, C1()
|
||||
|
||||
expect abstract class C2()
|
||||
expect interface I2
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
||||
expect interface S1
|
||||
expect interface S2
|
||||
|
||||
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED{JVM}!>open class A : S1, S2<!>
|
||||
open class A : S1, S2
|
||||
|
||||
class B : A()
|
||||
|
||||
|
||||
+6
@@ -33701,6 +33701,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt61166.kt")
|
||||
public void testKt61166() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noArgActualConstructor.kt")
|
||||
public void testNoArgActualConstructor() throws Exception {
|
||||
|
||||
+6
@@ -33701,6 +33701,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt61166.kt")
|
||||
public void testKt61166() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("noArgActualConstructor.kt")
|
||||
public void testNoArgActualConstructor() throws Exception {
|
||||
|
||||
+5
@@ -28702,6 +28702,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt61166.kt")
|
||||
public void testKt61166() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/multiplatform/kt61166.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noArgActualConstructor.kt")
|
||||
public void testNoArgActualConstructor() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt");
|
||||
|
||||
Reference in New Issue
Block a user