[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");
|
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
|
@Test
|
||||||
@TestMetadata("noArgActualConstructor.kt")
|
@TestMetadata("noArgActualConstructor.kt")
|
||||||
public void testNoArgActualConstructor() throws Exception {
|
public void testNoArgActualConstructor() throws Exception {
|
||||||
|
|||||||
+6
@@ -33701,6 +33701,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
|||||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
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
|
@Test
|
||||||
@TestMetadata("noArgActualConstructor.kt")
|
@TestMetadata("noArgActualConstructor.kt")
|
||||||
public void testNoArgActualConstructor() throws Exception {
|
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.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
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.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
|
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.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.name.ClassId
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.Name
|
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,
|
* 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) {
|
for (symbolFromSupertype in membersFromSupertype) {
|
||||||
val memberFromSupertype = symbolFromSupertype.owner as IrDeclaration
|
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 newMember = createFakeOverrideMember(listOf(memberFromSupertype), klass)
|
||||||
val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect(
|
val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect(
|
||||||
newMember.symbol,
|
newMember.symbol,
|
||||||
|
|||||||
@@ -627,63 +627,64 @@ class IrOverridingUtil(
|
|||||||
checkIsInlineFlag: Boolean,
|
checkIsInlineFlag: Boolean,
|
||||||
checkReturnType: Boolean
|
checkReturnType: Boolean
|
||||||
): OverrideCompatibilityInfo {
|
): 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?).
|
// The frontend goes into external overridability condition details here, but don't deal with them in IR (yet?).
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun isOverridableByWithoutExternalConditions(
|
fun IrTypeSystemContext.isOverridableByWithoutExternalConditions(
|
||||||
superMember: IrOverridableMember,
|
superMember: IrOverridableMember,
|
||||||
subMember: IrOverridableMember,
|
subMember: IrOverridableMember,
|
||||||
checkIsInlineFlag: Boolean,
|
checkIsInlineFlag: Boolean,
|
||||||
checkReturnType: Boolean
|
checkReturnType: Boolean
|
||||||
): OverrideCompatibilityInfo {
|
): OverrideCompatibilityInfo {
|
||||||
val superTypeParameters: List<IrTypeParameter>
|
val superTypeParameters: List<IrTypeParameter>
|
||||||
val subTypeParameters: List<IrTypeParameter>
|
val subTypeParameters: List<IrTypeParameter>
|
||||||
|
|
||||||
val superValueParameters: List<IrValueParameter>
|
val superValueParameters: List<IrValueParameter>
|
||||||
val subValueParameters: List<IrValueParameter>
|
val subValueParameters: List<IrValueParameter>
|
||||||
|
|
||||||
when (superMember) {
|
when (superMember) {
|
||||||
is IrSimpleFunction -> when {
|
is IrSimpleFunction -> when {
|
||||||
subMember !is IrSimpleFunction -> return incompatible("Member kind mismatch")
|
subMember !is IrSimpleFunction -> return incompatible("Member kind mismatch")
|
||||||
superMember.hasExtensionReceiver != subMember.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
superMember.hasExtensionReceiver != subMember.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
||||||
superMember.isSuspend != subMember.isSuspend -> return incompatible("Incompatible suspendability")
|
superMember.isSuspend != subMember.isSuspend -> return incompatible("Incompatible suspendability")
|
||||||
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline function can't be overridden")
|
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline function can't be overridden")
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
superTypeParameters = superMember.typeParameters
|
superTypeParameters = superMember.typeParameters
|
||||||
subTypeParameters = subMember.typeParameters
|
subTypeParameters = subMember.typeParameters
|
||||||
superValueParameters = superMember.compiledValueParameters
|
superValueParameters = superMember.compiledValueParameters
|
||||||
subValueParameters = subMember.compiledValueParameters
|
subValueParameters = subMember.compiledValueParameters
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is IrProperty -> when {
|
}
|
||||||
subMember !is IrProperty -> return incompatible("Member kind mismatch")
|
is IrProperty -> when {
|
||||||
superMember.getter.hasExtensionReceiver != subMember.getter.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
subMember !is IrProperty -> return incompatible("Member kind mismatch")
|
||||||
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline property can't be overridden")
|
superMember.getter.hasExtensionReceiver != subMember.getter.hasExtensionReceiver -> return incompatible("Receiver presence mismatch")
|
||||||
|
checkIsInlineFlag && superMember.isInline -> return incompatible("Inline property can't be overridden")
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
superTypeParameters = superMember.typeParameters
|
superTypeParameters = superMember.typeParameters
|
||||||
subTypeParameters = subMember.typeParameters
|
subTypeParameters = subMember.typeParameters
|
||||||
superValueParameters = superMember.compiledValueParameters
|
superValueParameters = superMember.compiledValueParameters
|
||||||
subValueParameters = subMember.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 {
|
superTypeParameters.size != subTypeParameters.size -> return incompatible("Type parameter number mismatch")
|
||||||
superMember.name != subMember.name -> {
|
superValueParameters.size != subValueParameters.size -> return incompatible("Value parameter number mismatch")
|
||||||
// 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")
|
// TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
|
||||||
superValueParameters.size != subValueParameters.size -> return incompatible("Value parameter number mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check the bounds. See OverridingUtil.areTypeParametersEquivalent()
|
|
||||||
// superTypeParameters.forEachIndexed { index, parameter ->
|
// superTypeParameters.forEachIndexed { index, parameter ->
|
||||||
// if (!AbstractTypeChecker.areTypeParametersEquivalent(
|
// if (!AbstractTypeChecker.areTypeParametersEquivalent(
|
||||||
// typeCheckerContext as AbstractTypeCheckerContext,
|
// typeCheckerContext as AbstractTypeCheckerContext,
|
||||||
@@ -693,34 +694,33 @@ class IrOverridingUtil(
|
|||||||
// ) return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch")
|
// ) return OverrideCompatibilityInfo.incompatible("Type parameter bounds mismatch")
|
||||||
// }
|
// }
|
||||||
|
|
||||||
val typeCheckerState = createIrTypeCheckerState(
|
val typeCheckerState = createIrTypeCheckerState(
|
||||||
IrTypeSystemContextWithAdditionalAxioms(
|
IrTypeSystemContextWithAdditionalAxioms(
|
||||||
typeSystem,
|
this,
|
||||||
superTypeParameters,
|
superTypeParameters,
|
||||||
subTypeParameters
|
subTypeParameters
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
superValueParameters.forEachIndexed { index, parameter ->
|
superValueParameters.forEachIndexed { index, parameter ->
|
||||||
if (!AbstractTypeChecker.equalTypes(
|
if (!AbstractTypeChecker.equalTypes(
|
||||||
typeCheckerState,
|
typeCheckerState,
|
||||||
subValueParameters[index].type,
|
subValueParameters[index].type,
|
||||||
parameter.type
|
parameter.type
|
||||||
)
|
)
|
||||||
) return incompatible("Value parameter type mismatch")
|
) return incompatible("Value parameter type mismatch")
|
||||||
}
|
|
||||||
|
|
||||||
if (checkReturnType) {
|
|
||||||
if (!AbstractTypeChecker.isSubtypeOf(
|
|
||||||
typeCheckerState,
|
|
||||||
subMember.returnType,
|
|
||||||
superMember.returnType
|
|
||||||
)
|
|
||||||
) return conflict("Return type mismatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
return success()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (checkReturnType) {
|
||||||
|
if (!AbstractTypeChecker.isSubtypeOf(
|
||||||
|
typeCheckerState,
|
||||||
|
subMember.returnType,
|
||||||
|
superMember.returnType
|
||||||
|
)
|
||||||
|
) return conflict("Return type mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
return success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val IrSimpleFunction?.hasExtensionReceiver: Boolean
|
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
|
expect interface I1
|
||||||
|
|
||||||
open class A : C1(), 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 abstract class C2()
|
||||||
expect interface I2
|
expect interface I2
|
||||||
|
|||||||
Vendored
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
expect interface S1
|
expect interface S1
|
||||||
expect interface S2
|
expect interface S2
|
||||||
|
|
||||||
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED{JVM}!>open class A : S1, S2<!>
|
open class A : S1, S2
|
||||||
|
|
||||||
class B : A()
|
class B : A()
|
||||||
|
|
||||||
|
|||||||
+6
@@ -33701,6 +33701,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
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
|
@Test
|
||||||
@TestMetadata("noArgActualConstructor.kt")
|
@TestMetadata("noArgActualConstructor.kt")
|
||||||
public void testNoArgActualConstructor() throws Exception {
|
public void testNoArgActualConstructor() throws Exception {
|
||||||
|
|||||||
+6
@@ -33701,6 +33701,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
|
|||||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
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
|
@Test
|
||||||
@TestMetadata("noArgActualConstructor.kt")
|
@TestMetadata("noArgActualConstructor.kt")
|
||||||
public void testNoArgActualConstructor() throws Exception {
|
public void testNoArgActualConstructor() throws Exception {
|
||||||
|
|||||||
+5
@@ -28702,6 +28702,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/multiplatform/kt60854.kt");
|
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")
|
@TestMetadata("noArgActualConstructor.kt")
|
||||||
public void testNoArgActualConstructor() throws Exception {
|
public void testNoArgActualConstructor() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt");
|
runTest("compiler/testData/codegen/box/multiplatform/noArgActualConstructor.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user