From 4d4ad6c0d8e550fa7c13fb7ed20ef7d7517f4a3d Mon Sep 17 00:00:00 2001 From: Vladimir Sukharev Date: Fri, 3 Nov 2023 20:07:17 +0100 Subject: [PATCH] [K/N] Fix K2 ObjC interop: Fields are not supported for Companion of subclass of ObjC type ^KT-63048 --- .../kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt | 6 ++++++ .../jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt | 12 ++++++++++++ .../jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt | 2 +- .../fir/backend/native/interop/FirObjCInterop.kt | 2 +- .../src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt | 9 +++++++++ .../konan/test/blackbox/CInteropCheckersTest.kt | 9 +++++---- 6 files changed, 34 insertions(+), 6 deletions(-) diff --git a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt index aad17cf5371..0fe2a971dd8 100644 --- a/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt +++ b/compiler/fir/fir2ir/jvm-backend/src/org/jetbrains/kotlin/fir/backend/jvm/JvmFir2IrExtensions.kt @@ -11,10 +11,12 @@ import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.JVMConfigurationKeys import org.jetbrains.kotlin.config.JvmSerializeIrMode import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.backend.Fir2IrComponents import org.jetbrains.kotlin.fir.backend.Fir2IrConversionScope import org.jetbrains.kotlin.fir.backend.Fir2IrExtensions import org.jetbrains.kotlin.fir.backend.InjectedValue +import org.jetbrains.kotlin.fir.declarations.FirProperty import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.declarations.* @@ -95,4 +97,8 @@ class JvmFir2IrExtensions( irDeserializer.deserializeTopLevelClass( irClass, components.irBuiltIns, components.symbolTable, components.irProviders, this ) + + override fun hasBackingField(property: FirProperty, session: FirSession): Boolean = + // NOTE maybe there might be platform-specific logic, similar to JvmGeneratorExtensionsImpl.isPropertyWithPlatformField() + Fir2IrExtensions.Default.hasBackingField(property, session) } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt index 9b23060d426..55a6c7cb85f 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrExtensions.kt @@ -5,6 +5,9 @@ package org.jetbrains.kotlin.fir.backend +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField import org.jetbrains.kotlin.fir.references.FirReference import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrMemberWithContainerSource @@ -20,6 +23,14 @@ interface Fir2IrExtensions { fun registerDeclarations(symbolTable: SymbolTable) fun findInjectedValue(calleeReference: FirReference, conversionScope: Fir2IrConversionScope): InjectedValue? + /** + * Platform-dependent logic to determine, whether a backing field is required. + * Should be called instead of `FirProperty.hasBackingField()` to decide whether to create a backing field. + * The implementation should return `true` in case platform-dependent condition for backing field existence is met, + * otherwise return a result of Fir2IrExtensions.Default.hasBackingField() + */ + fun hasBackingField(property: FirProperty, session: FirSession): Boolean + object Default : Fir2IrExtensions { override val irNeedsDeserialization: Boolean get() = false @@ -29,5 +40,6 @@ interface Fir2IrExtensions { override fun deserializeToplevelClass(irClass: IrClass, components: Fir2IrComponents): Boolean = false override fun registerDeclarations(symbolTable: SymbolTable) {} override fun findInjectedValue(calleeReference: FirReference, conversionScope: Fir2IrConversionScope) = null + override fun hasBackingField(property: FirProperty, session: FirSession): Boolean = property.hasBackingField } } \ No newline at end of file diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt index 3658ed0561e..5ec59cf8a28 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/lazy/Fir2IrLazyProperty.kt @@ -131,7 +131,7 @@ class Fir2IrLazyProperty( } } } - fir.hasBackingField && origin != IrDeclarationOrigin.FAKE_OVERRIDE -> { + extensions.hasBackingField(fir, session) && origin != IrDeclarationOrigin.FAKE_OVERRIDE -> { with(declarationStorage) { callablesGenerator.createBackingField( this@Fir2IrLazyProperty, diff --git a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt index e162921327e..47986a95554 100644 --- a/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt +++ b/compiler/fir/native/src/org/jetbrains/kotlin/fir/backend/native/interop/FirObjCInterop.kt @@ -141,7 +141,7 @@ internal fun FirFunction.getInitMethodIfObjCConstructor(session: FirSession, sco else this -internal fun FirProperty.isExternalObjCClassProperty(session: FirSession) = +fun FirProperty.isExternalObjCClassProperty(session: FirSession): Boolean = (containingClassLookupTag()?.toSymbol(session) as? FirClassSymbol)?.isExternalObjCClass(session) == true internal fun FirClassSymbol<*>.isExternalObjCClass(session: FirSession): Boolean = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt index 7325b1dd1b2..1bb3d854b73 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Fir2Ir.kt @@ -22,8 +22,12 @@ import org.jetbrains.kotlin.constant.EvaluatedConstTracker import org.jetbrains.kotlin.descriptors.isEmpty import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.backend.* import org.jetbrains.kotlin.fir.backend.native.FirNativeKotlinMangler +import org.jetbrains.kotlin.fir.backend.native.interop.isExternalObjCClassProperty +import org.jetbrains.kotlin.fir.declarations.FirProperty +import org.jetbrains.kotlin.fir.declarations.utils.isLateInit import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor import org.jetbrains.kotlin.fir.pipeline.convertToIrAndActualize import org.jetbrains.kotlin.fir.references.FirReference @@ -51,6 +55,11 @@ internal object NativeFir2IrExtensions : Fir2IrExtensions { override fun deserializeToplevelClass(irClass: IrClass, components: Fir2IrComponents) = false override fun registerDeclarations(symbolTable: SymbolTable) {} override fun findInjectedValue(calleeReference: FirReference, conversionScope: Fir2IrConversionScope) = null + override fun hasBackingField(property: FirProperty, session: FirSession): Boolean = + if (property.isExternalObjCClassProperty(session)) + property.isLateInit + else + Fir2IrExtensions.Default.hasBackingField(property, session) } internal fun PhaseContext.fir2Ir( diff --git a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CInteropCheckersTest.kt b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CInteropCheckersTest.kt index 60ea30ba09b..d4d65debca4 100644 --- a/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CInteropCheckersTest.kt +++ b/native/native.tests/tests/org/jetbrains/kotlin/konan/test/blackbox/CInteropCheckersTest.kt @@ -13,7 +13,6 @@ import org.jetbrains.kotlin.konan.test.blackbox.support.TestCompilerArgs import org.jetbrains.kotlin.konan.test.blackbox.support.TestKind import org.jetbrains.kotlin.konan.test.blackbox.support.compilation.TestCompilationResult.Companion.assertSuccess import org.jetbrains.kotlin.konan.test.blackbox.support.group.FirPipeline -import org.jetbrains.kotlin.konan.test.blackbox.support.runner.TestExecutable import org.jetbrains.kotlin.konan.test.blackbox.support.settings.PipelineType import org.jetbrains.kotlin.test.TestMetadata import org.junit.jupiter.api.Tag @@ -26,9 +25,12 @@ abstract class BaseCInteropCheckersTest : AbstractNativeSimpleTest() { @Test @TestMetadata(TEST_DATA_DIR) fun testBuilding() { + val defFile = File(TEST_DATA_DIR, DEF_FILE) + muteCInteropTestIfNecessary(defFile, targets.testTarget) + val library = cinteropToLibrary( targets = targets, - defFile = File(TEST_DATA_DIR, DEF_FILE), + defFile = defFile, outputDir = buildDir, freeCompilerArgs = TestCompilerArgs.EMPTY ).assertSuccess().resultingArtifact @@ -42,7 +44,6 @@ abstract class BaseCInteropCheckersTest : AbstractNativeSimpleTest() { val compilationResult = compileToExecutable(testCase, library.asLibraryDependency()).assertSuccess() } - companion object { const val TEST_DATA_DIR = "native/native.tests/testData/CInterop/KT-63048" const val DEF_FILE = "library.def" @@ -54,4 +55,4 @@ class CInteropCheckersTest : BaseCInteropCheckersTest() @FirPipeline @Tag("frontend-fir") -class FirCInteropCheckersTest : BaseCInteropCheckersTest() \ No newline at end of file +class FirCInteropCheckersTest : BaseCInteropCheckersTest()