diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt index 00e4f82c885..312ace2556f 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/pipeline/convertToIr.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.fir.pipeline +import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor import org.jetbrains.kotlin.builtins.DefaultBuiltIns @@ -45,16 +46,19 @@ fun FirResult.convertToIrAndActualize( val commonIrOutput = commonOutput.convertToIr( fir2IrExtensions, irGeneratorExtensions, - linkViaSignatures = true, + linkViaSignatures = linkViaSignatures, dependentComponents = emptyList() ) result = platformOutput.convertToIr( fir2IrExtensions, irGeneratorExtensions, - linkViaSignatures = true, + linkViaSignatures = linkViaSignatures, dependentComponents = listOf(commonIrOutput.components) ) - // TODO: implement IR actualization + IrActualizer.actualize( + result.irModuleFragment, + listOf(commonIrOutput.irModuleFragment) + ) } else { result = platformOutput.convertToIr( fir2IrExtensions, @@ -94,7 +98,8 @@ private fun ModuleCompilerAnalyzedOutput.convertToIr( JvmIrMangler, IrFactoryImpl, FirJvmVisibilityConverter, Fir2IrJvmSpecialAnnotationSymbolProvider(), irGeneratorExtensions, - kotlinBuiltIns = DefaultBuiltIns.Instance // TODO: consider passing externally + kotlinBuiltIns = DefaultBuiltIns.Instance, // TODO: consider passing externally, + dependentComponents = dependentComponents ) } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt index 224b51ccdbd..566fd938a2f 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt @@ -446,7 +446,7 @@ class Fir2IrConverter( session, scopeSession, firFiles, languageVersionSettings, fir2IrExtensions, mangler, irMangler, irFactory, visibilityConverter, specialSymbolProvider, irGenerationExtensions, - kotlinBuiltIns + kotlinBuiltIns, dependentComponents ) } val signatureComposer = FirBasedSignatureComposer(mangler, dependentComposers = dependentComponents.map { it.signatureComposer as FirBasedSignatureComposer }) @@ -472,7 +472,8 @@ class Fir2IrConverter( visibilityConverter: Fir2IrVisibilityConverter, specialSymbolProvider: Fir2IrSpecialSymbolProvider, irGenerationExtensions: Collection, - kotlinBuiltIns: KotlinBuiltIns + kotlinBuiltIns: KotlinBuiltIns, + dependentComponents: List ): Fir2IrResult { val signatureComposer = FirBasedSignatureComposer(mangler, dependentComposers = dependentComponents.map { it.signatureComposer as FirBasedSignatureComposer }) val signaturer = DescriptorSignatureComposerStub() @@ -481,7 +482,7 @@ class Fir2IrConverter( session, scopeSession, firFiles, languageVersionSettings, fir2IrExtensions, irMangler, irFactory, visibilityConverter, specialSymbolProvider, irGenerationExtensions, signatureComposer, - symbolTable, generateSignatures = false, kotlinBuiltIns = kotlinBuiltIns, dependentComponents = emptyList() + symbolTable, generateSignatures = false, kotlinBuiltIns = kotlinBuiltIns, dependentComponents = dependentComponents ) } diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index f990b280fcc..5ed0780c377 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -32691,6 +32691,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT @TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule") @TestDataPath("$PROJECT_ROOT") public class MultiModule { + @Test + @TestMetadata("accessToLocalClassFromBackend.kt") + public void testAccessToLocalClassFromBackend() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/accessToLocalClassFromBackend.kt"); + } + @Test public void testAllFilesPresentInMultiModule() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/multiModule"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); @@ -32750,6 +32756,24 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualTypealias.kt"); } + @Test + @TestMetadata("expectInterfaceInSupertypes.kt") + public void testExpectInterfaceInSupertypes() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectInterfaceInSupertypes.kt"); + } + + @Test + @TestMetadata("fakeOverridesInPlatformModule.kt") + public void testFakeOverridesInPlatformModule() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/fakeOverridesInPlatformModule.kt"); + } + + @Test + @TestMetadata("getRidOfDoubleBindingInFir2IrLazyProperty.kt") + public void testGetRidOfDoubleBindingInFir2IrLazyProperty() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt"); + } + @Test @TestMetadata("kt-51753-1.kt") public void testKt_51753_1() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt new file mode 100644 index 00000000000..ff8723eab34 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualCollector.kt @@ -0,0 +1,232 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.actualizer + +import org.jetbrains.kotlin.backend.common.ir.isProperExpect +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.types.classifierOrFail +import org.jetbrains.kotlin.ir.util.kotlinFqName +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.FqName + +internal class ExpectActualCollector(private val mainFragment: IrModuleFragment, private val dependentFragments: List) { + fun collect(): Map { + val result = mutableMapOf() + // Collect and link classifiers at first to make it possible to expand type aliases on the callables linking + val (allActualDeclarations, typeAliasMap) = result.appendExpectActualClassifiersMap() + result.appendExpectActualCallablesMap(allActualDeclarations, typeAliasMap, dependentFragments) + return result + } + + private fun MutableMap.appendExpectActualClassifiersMap(): Pair, Map> { + val actualClassifiers = mutableMapOf() + // There is no list for builtins declarations, that's why they are being collected from typealiases + val allActualDeclarations = mutableSetOf() + val typeAliasMap = mutableMapOf() // It's used to link members from expect class that have typealias actual + + ActualClassifiersCollector(actualClassifiers, allActualDeclarations, typeAliasMap).visitModuleFragment(mainFragment) + + val linkCollector = ClassifiersLinkCollector(this, actualClassifiers) + dependentFragments.forEach { linkCollector.visitModuleFragment(it) } + + return allActualDeclarations to typeAliasMap + } + + class ActualClassifiersCollector( + private val actualClassifiers: MutableMap, + private val allActualClassifiers: MutableSet, + private val typeAliasMap: MutableMap + ) : IrElementVisitorVoid { + override fun visitTypeAlias(declaration: IrTypeAlias) { + if (declaration.isActual) { + val expandedTypeSymbol = declaration.expandedType.classifierOrFail + actualClassifiers[declaration.kotlinFqName] = expandedTypeSymbol + if (expandedTypeSymbol is IrClassSymbol) { + allActualClassifiers.add(expandedTypeSymbol.owner) + typeAliasMap[declaration.kotlinFqName] = expandedTypeSymbol.owner.kotlinFqName + } + } + visitElement(declaration) + } + + override fun visitClass(declaration: IrClass) { + if (!declaration.isExpect) { + actualClassifiers[declaration.kotlinFqName] = declaration.symbol + } + visitDeclaration(declaration) + } + + override fun visitEnumEntry(declaration: IrEnumEntry) { + if (!declaration.isProperExpect) { + actualClassifiers[FqName.fromSegments( + listOf( + declaration.parent.kotlinFqName.asString(), + declaration.name.asString() + ) + )] = declaration.symbol + } + visitDeclaration(declaration) + } + + override fun visitTypeParameter(declaration: IrTypeParameter) { + if (!declaration.isProperExpect) { + actualClassifiers[FqName.fromSegments( + listOf(declaration.parent.kotlinFqName.asString(), declaration.name.asString()) + )] = declaration.symbol + } + visitDeclaration(declaration) + } + + override fun visitDeclaration(declaration: IrDeclarationBase) { + if (!declaration.isProperExpect) { + allActualClassifiers.add(declaration) + } + visitElement(declaration) + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + } + + class ClassifiersLinkCollector( + private val expectActualMap: MutableMap, + private val actualClassifiers: Map + ) : IrElementVisitorVoid { + private fun addLinkOrReportMissing(expectElement: IrSymbolOwner, actualTypeId: FqName) { + val actualClassifier = actualClassifiers[actualTypeId] + if (actualClassifier != null) { + expectActualMap[expectElement.symbol] = actualClassifier + } else { + reportMissingActual(expectElement) + } + } + + override fun visitClass(declaration: IrClass) { + if (declaration.isExpect) { + addLinkOrReportMissing(declaration, declaration.kotlinFqName) + } + visitElement(declaration) + } + + override fun visitEnumEntry(declaration: IrEnumEntry) { + if (declaration.isProperExpect) { + addLinkOrReportMissing( + declaration, FqName.fromSegments(listOf(declaration.parent.kotlinFqName.asString(), declaration.name.asString())) + ) + } + visitElement(declaration) + } + + override fun visitTypeParameter(declaration: IrTypeParameter) { + if (declaration.isProperExpect) { + addLinkOrReportMissing( + declaration, + FqName.fromSegments(listOf(declaration.parent.kotlinFqName.asString(), declaration.name.asString())) + ) + } + visitElement(declaration) + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + } + + private fun MutableMap.appendExpectActualCallablesMap( + allActualDeclarations: Set, + typeAliasMap: Map, + dependentFragments: List + ) { + val actualFunctions = mutableMapOf>() + val actualProperties = mutableMapOf() + + collectActualCallables(actualFunctions, actualProperties, allActualDeclarations) + val collector = CallablesLinkCollector(this, actualFunctions, actualProperties, typeAliasMap) + dependentFragments.forEach { collector.visitModuleFragment(it) } + } + + private fun collectActualCallables( + actualFunctions: MutableMap>, + actualProperties: MutableMap, + allActualDeclarations: Set + ) { + fun collectActualsCallables(declaration: IrDeclaration) { + when (declaration) { + is IrFunction -> { + actualFunctions.getOrPut(CallableId(declaration.parent.kotlinFqName, declaration.name)) { + mutableListOf() + }.add(declaration) + } + is IrProperty -> { + actualProperties.getOrPut(CallableId(declaration.parent.kotlinFqName, declaration.name)) { + declaration + } + } + is IrClass -> { + for (member in declaration.declarations) { + collectActualsCallables(member) + } + } + } + } + + for (actualDeclaration in allActualDeclarations) { + collectActualsCallables(actualDeclaration) + } + } + + class CallablesLinkCollector( + private val expectActualMap: MutableMap, + private val actualFunctions: MutableMap>, + private val actualProperties: MutableMap, + private val typeAliasMap: Map + ) : IrElementVisitorVoid { + private fun actualizeCallable(declaration: IrDeclarationWithName): CallableId { + val fullName = declaration.parent.kotlinFqName + return CallableId(typeAliasMap[fullName] ?: fullName, declaration.name) + } + + override fun visitFunction(declaration: IrFunction) { + if (!declaration.isExpect) return + val functions = actualFunctions[actualizeCallable(declaration)] + var isActualFunctionFound = false + if (functions != null) { + for (actualFunction in functions) { + if (checkParameters(declaration, actualFunction, expectActualMap)) { + expectActualMap[declaration.symbol] = actualFunction.symbol + isActualFunctionFound = true + break + } + } + } + if (!isActualFunctionFound) { + reportMissingActual(declaration) + } + } + + override fun visitProperty(declaration: IrProperty) { + if (!declaration.isExpect) return + val properties = actualProperties[actualizeCallable(declaration)] + if (properties != null) { + expectActualMap[declaration.symbol] = properties.symbol + declaration.getter?.symbol?.let { expectActualMap[it] = properties.getter!!.symbol } + declaration.setter?.symbol?.let { expectActualMap[it] = properties.setter!!.symbol } + } else { + reportMissingActual(declaration) + } + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualLinker.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualLinker.kt new file mode 100644 index 00000000000..2714ed44ef9 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/ExpectActualLinker.kt @@ -0,0 +1,161 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.actualizer + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.symbols.* +import org.jetbrains.kotlin.ir.util.DeepCopyIrTreeWithSymbols +import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper +import org.jetbrains.kotlin.ir.util.SymbolRemapper +import org.jetbrains.kotlin.ir.util.SymbolRenamer + +class ExpectActualLinker(private val expectActualMap: Map) { + private val symbolRemapper = object : SymbolRemapper { + override fun getDeclaredClass(symbol: IrClassSymbol) = symbol + + override fun getDeclaredScript(symbol: IrScriptSymbol) = symbol + + override fun getDeclaredFunction(symbol: IrSimpleFunctionSymbol) = symbol + + override fun getDeclaredProperty(symbol: IrPropertySymbol) = symbol + + override fun getDeclaredField(symbol: IrFieldSymbol) = symbol + + override fun getDeclaredFile(symbol: IrFileSymbol) = symbol + + override fun getDeclaredConstructor(symbol: IrConstructorSymbol) = symbol + + override fun getDeclaredEnumEntry(symbol: IrEnumEntrySymbol) = symbol + + override fun getDeclaredExternalPackageFragment(symbol: IrExternalPackageFragmentSymbol) = symbol + + override fun getDeclaredVariable(symbol: IrVariableSymbol) = symbol + + override fun getDeclaredLocalDelegatedProperty(symbol: IrLocalDelegatedPropertySymbol) = symbol + + override fun getDeclaredTypeParameter(symbol: IrTypeParameterSymbol) = symbol + + override fun getDeclaredValueParameter(symbol: IrValueParameterSymbol) = symbol + + override fun getDeclaredTypeAlias(symbol: IrTypeAliasSymbol) = symbol + + override fun getReferencedClass(symbol: IrClassSymbol) = symbol.actualizeSymbol() + + override fun getReferencedScript(symbol: IrScriptSymbol) = symbol.actualizeSymbol() + + override fun getReferencedClassOrNull(symbol: IrClassSymbol?) = symbol?.actualizeSymbol() + + override fun getReferencedEnumEntry(symbol: IrEnumEntrySymbol) = symbol.actualizeSymbol() + + override fun getReferencedVariable(symbol: IrVariableSymbol) = symbol.actualizeSymbol() + + override fun getReferencedLocalDelegatedProperty(symbol: IrLocalDelegatedPropertySymbol) = symbol.actualizeSymbol() + + override fun getReferencedField(symbol: IrFieldSymbol) = symbol.actualizeSymbol() + + override fun getReferencedConstructor(symbol: IrConstructorSymbol) = symbol.actualizeSymbol() + + override fun getReferencedValue(symbol: IrValueSymbol) = symbol.actualizeSymbol() + + override fun getReferencedFunction(symbol: IrFunctionSymbol) = symbol.actualizeSymbol() + + override fun getReferencedProperty(symbol: IrPropertySymbol) = symbol.actualizeSymbol() + + override fun getReferencedSimpleFunction(symbol: IrSimpleFunctionSymbol) = symbol.actualizeSymbol() + + override fun getReferencedReturnableBlock(symbol: IrReturnableBlockSymbol) = symbol.actualizeSymbol() + + override fun getReferencedClassifier(symbol: IrClassifierSymbol) = symbol.actualizeSymbol() + + override fun getReferencedTypeAlias(symbol: IrTypeAliasSymbol) = symbol.actualizeSymbol() + + private inline fun S.actualizeSymbol(): S = (expectActualMap[this] as? S) ?: this + } + + private val typeRemapper = DeepCopyTypeRemapper(symbolRemapper) + + private val actualizer = object : DeepCopyIrTreeWithSymbols(symbolRemapper, typeRemapper, SymbolRenamer.DEFAULT) { + override fun visitModuleFragment(declaration: IrModuleFragment) = + declaration.also { it.transformChildren(this, null) } + + override fun visitExternalPackageFragment(declaration: IrExternalPackageFragment) = + declaration.also { it.transformChildren(this, null) } + + override fun visitFile(declaration: IrFile) = + declaration.also { it.transformChildren(this, null) } + + override fun visitScript(declaration: IrScript) = + declaration.also { + it.baseClass = it.baseClass?.remapType() + it.transformChildren(this, null) + } + + override fun visitClass(declaration: IrClass) = + declaration.also { + it.superTypes = it.superTypes.map { superType -> superType.remapType() } + it.transformChildren(this, null) + } + + override fun visitSimpleFunction(declaration: IrSimpleFunction) = visitFunction(declaration) as IrSimpleFunction + + override fun visitConstructor(declaration: IrConstructor) = visitFunction(declaration) as IrConstructor + + override fun visitFunction(declaration: IrFunction) = + declaration.also { + it.returnType = it.returnType.remapType() + it.transformChildren(this, null) + } + + override fun visitProperty(declaration: IrProperty) = + declaration.also { it.transformChildren(this, null) } + + override fun visitField(declaration: IrField) = + declaration.also { + it.type = it.type.remapType() + it.transformChildren(this, null) + } + + override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty) = + declaration.also { + it.type = it.type.remapType() + it.transformChildren(this, null) + } + + override fun visitEnumEntry(declaration: IrEnumEntry) = + declaration.also { it.transformChildren(this, null) } + + override fun visitTypeParameter(declaration: IrTypeParameter) = + declaration.also { + it.superTypes = it.superTypes.map { superType -> superType.remapType() } + it.transformChildren(this, null) + } + + override fun visitValueParameter(declaration: IrValueParameter) = + declaration.also { + it.type = it.type.remapType() + it.varargElementType = it.varargElementType?.remapType() + it.transformChildren(this, null) + } + + override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer) = + declaration.also { it.transformChildren(this, null) } + + override fun visitVariable(declaration: IrVariable) = + declaration.also { + it.type = it.type.remapType() + it.transformChildren(this, null) + } + + override fun visitTypeAlias(declaration: IrTypeAlias) = + declaration.also { + it.expandedType = it.expandedType.remapType() + it.transformChildren(this, null) + } + } + + fun actualize(irElement: IrElement) = irElement.transform(actualizer, null) +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt new file mode 100644 index 00000000000..ae4853d8fd2 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizer.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.actualizer + +import org.jetbrains.kotlin.backend.common.ir.isProperExpect +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.symbols.IrSymbol + +object IrActualizer { + fun actualize(mainFragment: IrModuleFragment, dependentFragments: List) { + val expectActualMap = ExpectActualCollector(mainFragment, dependentFragments).collect() + removeExpectDeclaration(dependentFragments) // TODO: consider removing this call. See ExpectDeclarationRemover.kt + addMissingFakeOverrides(expectActualMap, dependentFragments) + linkExpectToActual(expectActualMap, dependentFragments) + mergeIrFragments(mainFragment, dependentFragments) + } + + private fun removeExpectDeclaration(dependentFragments: List) { + for (fragment in dependentFragments) { + for (file in fragment.files) { + file.declarations.removeAll { it.isProperExpect } + } + } + } + + private fun addMissingFakeOverrides(expectActualMap: Map, dependentFragments: List) { + MissingFakeOverridesAdder(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } } + } + + private fun linkExpectToActual(expectActualMap: Map, dependentFragments: List) { + ExpectActualLinker(expectActualMap).apply { dependentFragments.forEach { actualize(it) } } + } + + private fun mergeIrFragments(mainFragment: IrModuleFragment, dependentFragments: List) { + mainFragment.files.addAll(dependentFragments.flatMap { it.files }) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizerUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizerUtils.kt new file mode 100644 index 00000000000..8f4547c62e4 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/IrActualizerUtils.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.actualizer + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclarationWithName +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.types.classifierOrFail +import org.jetbrains.kotlin.ir.util.render + +fun checkParameters( + expectFunction: IrFunction, + actualFunction: IrFunction, + expectActualTypesMap: Map +): Boolean { + if (expectFunction.valueParameters.size != actualFunction.valueParameters.size) return false + for ((expectParameter, actualParameter) in expectFunction.valueParameters.zip(actualFunction.valueParameters)) { + val expectParameterTypeSymbol = expectParameter.type.classifierOrFail + val actualizedParameterTypeSymbol = expectActualTypesMap[expectParameterTypeSymbol] ?: expectParameterTypeSymbol + if (actualizedParameterTypeSymbol != actualParameter.type.classifierOrFail) { + return false + } + } + return true +} + +fun reportMissingActual(irElement: IrElement) { + // TODO: set up diagnostics reporting + throw AssertionError("Missing actual for ${irElement.render()}") +} + +fun reportManyInterfacesMembersNotImplemented(declaration: IrClass, actualMember: IrDeclarationWithName) { + // TODO: set up diagnostics reporting + throw AssertionError("${declaration.name} must override ${actualMember.name} because it inherits multiple interface methods of it") +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/MissingFakeOverridesAdder.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/MissingFakeOverridesAdder.kt new file mode 100644 index 00000000000..18831f30a97 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/actualizer/MissingFakeOverridesAdder.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.actualizer + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl +import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl +import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl +import org.jetbrains.kotlin.ir.types.classifierOrFail +import org.jetbrains.kotlin.ir.types.isAny +import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid + +class MissingFakeOverridesAdder(private val expectActualMap: Map) : IrElementVisitorVoid { + override fun visitClass(declaration: IrClass) { + if (!declaration.isExpect) { + processSupertypes(declaration, expectActualMap) + } + visitElement(declaration) + } + + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } +} + +private fun processSupertypes(declaration: IrClass, expectActualMap: Map) { + val members by lazy(LazyThreadSafetyMode.NONE) { + declaration.declarations.filter { !it.isBuiltinMember() }.filterIsInstance() + .groupBy { it.name } + } + + for (superType in declaration.superTypes) { + val actualClass = expectActualMap[superType.classifierOrFail]?.owner as? IrClass ?: continue + for (actualMember in actualClass.declarations) { + if (actualMember.isBuiltinMember()) continue + when (actualMember) { + is IrFunctionImpl -> { + val existingMembers = members[actualMember.name] + + var isActualFunctionFound = false + if (existingMembers != null) { + for (existingMember in existingMembers) { + if (existingMember is IrFunction) { + if (checkParameters(existingMember, actualMember, expectActualMap)) { + isActualFunctionFound = true + break + } + } + } + } + + if (isActualFunctionFound) { + reportManyInterfacesMembersNotImplemented(declaration, actualMember) + continue + } + + declaration.declarations.add(createFakeOverrideFunction(actualMember, declaration)) + } + is IrPropertyImpl -> { + if (members[actualMember.name] != null) { + reportManyInterfacesMembersNotImplemented(declaration, actualMember) + continue + } + + declaration.declarations.add(createFakeOverrideProperty(actualMember, declaration)) + } + } + } + } +} + +private fun IrDeclaration.isBuiltinMember(): Boolean { + if (this !is IrFunction) return false + return this is IrConstructor || dispatchReceiverParameter?.type?.isAny() == true +} + +private fun createFakeOverrideProperty(actualMember: IrPropertyImpl, declaration: IrClass) = + IrPropertyImpl( + actualMember.startOffset, + actualMember.endOffset, + IrDeclarationOrigin.FAKE_OVERRIDE, + IrPropertySymbolImpl(), + actualMember.name, + actualMember.visibility, + actualMember.modality, + actualMember.isVar, + actualMember.isConst, + actualMember.isLateinit, + actualMember.isDelegated, + isExternal = actualMember.isExternal + ).also { + it.parent = declaration + it.annotations = actualMember.annotations + it.backingField = actualMember.backingField + it.getter = (actualMember.getter as? IrFunctionImpl)?.let { getter -> + createFakeOverrideFunction(getter, declaration, it.symbol) + } + it.setter = (actualMember.setter as? IrFunctionImpl)?.let { setter -> + createFakeOverrideFunction(setter, declaration, it.symbol) + } + it.overriddenSymbols = listOf(actualMember.symbol) + it.metadata = actualMember.metadata + it.attributeOwnerId = it + } + +private fun createFakeOverrideFunction( + actualFunction: IrFunctionImpl, + parent: IrDeclarationParent, + correspondingPropertySymbol: IrPropertySymbol? = null +) = + IrFunctionImpl( + actualFunction.startOffset, + actualFunction.endOffset, + IrDeclarationOrigin.FAKE_OVERRIDE, + IrSimpleFunctionSymbolImpl(), + actualFunction.name, + actualFunction.visibility, + actualFunction.modality, + actualFunction.returnType, + actualFunction.isInline, + actualFunction.isExternal, + actualFunction.isTailrec, + actualFunction.isSuspend, + actualFunction.isOperator, + actualFunction.isInfix, + isExpect = false + ).also { + it.parent = parent + it.annotations = actualFunction.annotations.map { p -> p.deepCopyWithSymbols(it) } + it.typeParameters = actualFunction.typeParameters.map { p -> p.deepCopyWithSymbols(it) } + it.dispatchReceiverParameter = actualFunction.dispatchReceiverParameter?.deepCopyWithSymbols(it) + it.extensionReceiverParameter = actualFunction.extensionReceiverParameter?.deepCopyWithSymbols(it) + it.valueParameters = actualFunction.valueParameters.map { p -> p.deepCopyWithSymbols(it) } + it.contextReceiverParametersCount = actualFunction.contextReceiverParametersCount + it.metadata = actualFunction.metadata + it.overriddenSymbols = listOf(actualFunction.symbol) + it.attributeOwnerId = it + it.correspondingPropertySymbol = correspondingPropertySymbol + } \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt index becc5028ff6..71505ebf9e1 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/descriptorBasedUtils.kt @@ -15,7 +15,6 @@ val IrDeclaration.isExpect // The original isExpect represents what user has written. // This predicate means "there can possibly exist an 'actual' for the given declaration". // Shouldn't it be incorporated to descriptor -> ir declaration psi2ir translation phase? -@Suppress("unused") val IrDeclaration.isProperExpect: Boolean get() = this is IrClass && isExpect || this is IrFunction && isExpect || diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.out b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.out index 7209222f18f..dc56f448423 100644 --- a/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.out +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithError.out @@ -11,5 +11,11 @@ compiler or generated code. Use it at your own risk! warning: ATTENTION! This build uses experimental K2 compiler: -Xuse-k2 -error: k2 compiler does not support multi-platform projects yet, so please remove -Xuse-k2 flag +compiler/testData/cli/jvm/firMultiplatformCompilationWithError/jvm.kt:1:18: error: actual class 'actual interface A : Any' has no corresponding members for expected class members: + + expect fun foo(): Unit + + +actual interface A + ^ COMPILATION_ERROR diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithLightTreeWithoutErrors.args b/compiler/testData/cli/jvm/firMultiplatformCompilationWithLightTreeWithoutErrors.args new file mode 100644 index 00000000000..b519c31e96f --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithLightTreeWithoutErrors.args @@ -0,0 +1,11 @@ +$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/common.kt +$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/jvm.kt +-Xcommon-sources +$TESTDATA_DIR$/firMultiplatformCompilationWithoutErrors/common.kt +-Xuse-k2 +-cp +. +-d +$TEMP_DIR$ +-XXLanguage\:+MultiPlatformProjects +-Xuse-fir-lt diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithLightTreeWithoutErrors.out b/compiler/testData/cli/jvm/firMultiplatformCompilationWithLightTreeWithoutErrors.out new file mode 100644 index 00000000000..12cc44ccf11 --- /dev/null +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithLightTreeWithoutErrors.out @@ -0,0 +1,14 @@ +warning: advanced option value is passed in an obsolete form. Please use the '=' character to specify the value: -Xcommon-sources=... +warning: ATTENTION! +This build uses unsafe internal compiler arguments: + +-XXLanguage:+MultiPlatformProjects + +This mode is not recommended for production use, +as no stability/compatibility guarantees are given on +compiler or generated code. Use it at your own risk! + +warning: ATTENTION! + This build uses experimental K2 compiler: + -Xuse-k2 +OK diff --git a/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.out b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.out index 7209222f18f..12cc44ccf11 100644 --- a/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.out +++ b/compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.out @@ -11,5 +11,4 @@ compiler or generated code. Use it at your own risk! warning: ATTENTION! This build uses experimental K2 compiler: -Xuse-k2 -error: k2 compiler does not support multi-platform projects yet, so please remove -Xuse-k2 flag -COMPILATION_ERROR +OK diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/accessToLocalClassFromBackend.kt b/compiler/testData/codegen/box/multiplatform/multiModule/accessToLocalClassFromBackend.kt new file mode 100644 index 00000000000..6ae5fb807af --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/accessToLocalClassFromBackend.kt @@ -0,0 +1,18 @@ +// TARGET_BACKEND: JVM +// !LANGUAGE: +MultiPlatformProjects + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +val LocalClass = object { + override fun toString() = "OK" +} + +fun ok() = LocalClass.toString() + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +fun box() = ok() \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/correctParentForTypeParameter.kt b/compiler/testData/codegen/box/multiplatform/multiModule/correctParentForTypeParameter.kt new file mode 100644 index 00000000000..bf302dbdde3 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/correctParentForTypeParameter.kt @@ -0,0 +1,20 @@ +// TARGET_BACKEND: JVM +// !LANGUAGE: +MultiPlatformProjects +// MODULE: lib +// FILE: lib.kt + +package foo + +fun transform(x: String, f: (String) -> String): String { + return f(x) + "K" +} + +// MODULE: lib2()()(lib) +// TARGET_BACKEND: JVM_IR +// FILE: main.kt + +package bar + +import foo.* + +fun box() = transform("") { "O" } \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectActualCallableReference.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualCallableReference.kt new file mode 100644 index 00000000000..0db6f86279a --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualCallableReference.kt @@ -0,0 +1,22 @@ +// TARGET_BACKEND: JVM_IR +// !LANGUAGE: +MultiPlatformProjects + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: commonMain.kt + +expect class R + +expect fun ret(): R + +fun foo() = ::ret + +// MODULE: main()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +actual fun ret(): R = "OK" + +actual typealias R = String + +fun box() = foo().invoke() \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectActualDifferentPackages.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualDifferentPackages.kt new file mode 100644 index 00000000000..22d9f47246e --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualDifferentPackages.kt @@ -0,0 +1,53 @@ +// TARGET_BACKEND: JVM_IR +// !LANGUAGE: +MultiPlatformProjects +// MODULE: common +// TARGET_PLATFORM: Common + +// FILE: p1.kt + +package p1 + +expect fun f(): String + +expect class A() { + fun g(): Boolean +} + +fun test() = A().g() + +// FILE: p2.kt + +package p2 + +expect fun f(): String + +expect class A() { + fun g(): Int +} + +// MODULE: main()()(common) +// TARGET_PLATFORM: JVM + +// FILE: p11.kt + +package p1 + +actual fun f() = "O" + +actual class A { + actual fun g() = true +} + +// FILE: p22.kt + +package p2 + +actual fun f() = "K" + +actual class A { + actual fun g() = 42 +} + +// FILE: main.kt + +fun box() = if (p1.A().g()) p1.f() + p2.f() else "fail" \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectActualFakeOverrides.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualFakeOverrides.kt new file mode 100644 index 00000000000..d9948cb76f8 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualFakeOverrides.kt @@ -0,0 +1,30 @@ +// TARGET_BACKEND: JVM_IR +// !LANGUAGE: +MultiPlatformProjects +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: commonMain.kt + +expect class A() { + fun foo(s: String): String + + val bar: String +} + +fun test(s: String): String { + val a = A() + return a.foo(s) + a.bar +} + +// MODULE: main()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +open class B { + fun foo(s: String) = s + + val bar: String = "K" +} + +actual class A : B() + +fun box() = test("O") \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectActualMultiCommon.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualMultiCommon.kt new file mode 100644 index 00000000000..475598186bd --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualMultiCommon.kt @@ -0,0 +1,36 @@ +// TARGET_BACKEND: JVM +// !LANGUAGE: +MultiPlatformProjects + +// MODULE: common0 +// TARGET_PLATFORM: Common +// FILE: common0.kt + +expect fun f0(): Boolean + +fun g0() = f0() + +// MODULE: common1()()(common0) +// TARGET_PLATFORM: Common +// FILE: common1.kt + +expect fun f1(): String + +fun g1() = f1() + +// MODULE: common2()()(common0) +// TARGET_PLATFORM: Common +// FILE: common2.kt + +expect fun f2(): String + +fun g2() = f2() + +// MODULE: jvm()()(common1, common2) +// TARGET_PLATFORM: JVM +// FILE: jvm.kt + +actual fun f0(): Boolean = true +actual fun f1(): String = "O" +actual fun f2(): String = "K" + +fun box() = if (g0()) g1() + g2() else "fail" \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectActualOverloads.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualOverloads.kt new file mode 100644 index 00000000000..c6a53964f28 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualOverloads.kt @@ -0,0 +1,25 @@ +// TARGET_BACKEND: JVM_IR +// !LANGUAGE: +MultiPlatformProjects +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: commonMain.kt + +expect class S + +expect fun foo(s: S): S + +expect fun foo(i: Int): Int + +fun test(s: S) = foo(s) + +// MODULE: main()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +actual fun foo(i: Int) = i + +actual fun foo(s: String) = s + +actual typealias S = String + +fun box() = test("OK") \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimple.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimple.kt new file mode 100644 index 00000000000..dabcb947a14 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimple.kt @@ -0,0 +1,25 @@ +// TARGET_BACKEND: JVM +// !LANGUAGE: +MultiPlatformProjects + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +expect fun func(): String + +expect var prop: String + +fun test(): String { + prop = "K" + return func() + prop +} + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +actual fun func(): String = "O" + +actual var prop: String = "!" + +fun box() = test() \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectActualTypealias.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualTypealias.kt new file mode 100644 index 00000000000..d9ee4ca5035 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectActualTypealias.kt @@ -0,0 +1,24 @@ +// TARGET_BACKEND: JVM_IR +// !LANGUAGE: +MultiPlatformProjects + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +expect class S { + val length: Int +} + +expect fun foo(): S + +fun test(): S = foo() + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +actual typealias S = String + +actual fun foo(): S = "OK" + +fun box() = test() \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/expectInterfaceInSupertypes.kt b/compiler/testData/codegen/box/multiplatform/multiModule/expectInterfaceInSupertypes.kt new file mode 100644 index 00000000000..e6e5f356dc7 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/expectInterfaceInSupertypes.kt @@ -0,0 +1,40 @@ +// TARGET_BACKEND: JVM +// !LANGUAGE: +MultiPlatformProjects + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +expect interface S1 +expect interface S2 + +expect class S + +open class A : S1, S2 + +class B : A() + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +actual interface S1 { + fun o(): S = "O" + val p: Boolean + get() = true +} + +actual interface S2 { + fun k() = "K" +} + +actual typealias S = String + +fun box(): String { + val b = B() + return if (b.p) { + b.o() + b.k() + } else { + "FAIL" + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/fakeOverridesInPlatformModule.kt b/compiler/testData/codegen/box/multiplatform/multiModule/fakeOverridesInPlatformModule.kt new file mode 100644 index 00000000000..7d75602cfd4 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/fakeOverridesInPlatformModule.kt @@ -0,0 +1,22 @@ +// TARGET_BACKEND: JVM +// !LANGUAGE: +MultiPlatformProjects + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +interface Foo { + fun ok(): String = "OK" +} + +fun test(e: Foo) = e.ok() + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +interface Bar : Foo + +class A : Bar + +fun box() = A().ok() diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt b/compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt new file mode 100644 index 00000000000..4a974b2e191 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt @@ -0,0 +1,25 @@ +// TARGET_BACKEND: JVM +// !LANGUAGE: +MultiPlatformProjects +// WITH_REFLECT + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common.kt + +interface I { + companion object { + const val OK: String = "OK" + } +} + +fun ok() = I.OK + +// MODULE: jvm()()(common) +// TARGET_PLATFORM: JVM +// FILE: main.kt + +import kotlin.reflect.KType + +fun getAnnotations(kType: KType) = kType.annotations + +fun box() = ok() \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/kt-51753-1.kt b/compiler/testData/codegen/box/multiplatform/multiModule/kt-51753-1.kt new file mode 100644 index 00000000000..2f97fb6ac88 --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/kt-51753-1.kt @@ -0,0 +1,56 @@ +// ISSUE: KT-51753 +// !LANGUAGE: +MultiPlatformProjects +// TARGET_BACKEND: JVM_IR +// WITH_STDLIB +// WITH_REFLECT + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common/AtomicBoolean.kt + +import kotlin.reflect.KProperty + +expect class AtomicBoolean { + var value: Boolean + + inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean + + inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) +} + +expect fun atomic(initial: Boolean): AtomicBoolean + +// FILE: common/test.kt + +private val _topLevelBoolean = atomic(false) +var topLevelDelegatedPropertyBoolean: Boolean by _topLevelBoolean + +// MODULE: main()()(common) +// TARGET_PLATFORM: JVM +// FILE: jvm/AtomicBoolean.kt + +import kotlin.reflect.KProperty + +actual class AtomicBoolean internal constructor(v: Boolean) { + + @Volatile + private var _value: Int = if (v) 1 else 0 + + actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean = value + + actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { + this.value = value + } + + actual var value: Boolean + get() = _value != 0 + set(value) { + _value = if (value) 1 else 0 + } +} + +actual fun atomic(initial: Boolean): AtomicBoolean = AtomicBoolean(initial) + +// FILE: jvm/box.kt + +fun box(): String = if (!topLevelDelegatedPropertyBoolean) "OK" else "FAIL (true)" \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/multiModule/kt-51753-2.kt b/compiler/testData/codegen/box/multiplatform/multiModule/kt-51753-2.kt new file mode 100644 index 00000000000..c5d7336a63a --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/multiModule/kt-51753-2.kt @@ -0,0 +1,52 @@ +// ISSUE: KT-51753 +// !LANGUAGE: +MultiPlatformProjects +// TARGET_BACKEND: JVM_IR +// WITH_STDLIB +// WITH_REFLECT + +// MODULE: common +// TARGET_PLATFORM: Common +// FILE: common/AtomicBoolean.kt + +import kotlin.reflect.KProperty + +expect class AtomicRef { + var value: T + + inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T + + inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) +} + +expect fun atomic(initial: T): AtomicRef + +// FILE: common/test.kt + +private val _topLevelRef = atomic("A") +var topLevelDelegatedPropertyRef: String by _topLevelRef + +// MODULE: main()()(common) +// TARGET_PLATFORM: JVM +// FILE: jvm/AtomicBoolean.kt + +import kotlin.reflect.KProperty + +actual class AtomicRef internal constructor(v: T) { + + actual inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value + + actual inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { + this.value = value + } + + actual var value: T = v +} + +actual fun atomic(initial: T): AtomicRef = AtomicRef(initial) + +// FILE: jvm/box.kt + +fun box(): String { + val s = topLevelDelegatedPropertyRef + return if (s == "A") "OK" else "FAIL($s)" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/multiplatform/starImportOfExpectEnumWithActualTypeAlias.kt b/compiler/testData/codegen/box/multiplatform/starImportOfExpectEnumWithActualTypeAlias.kt index 9d7e94ec842..e9d1b2a3bd3 100644 --- a/compiler/testData/codegen/box/multiplatform/starImportOfExpectEnumWithActualTypeAlias.kt +++ b/compiler/testData/codegen/box/multiplatform/starImportOfExpectEnumWithActualTypeAlias.kt @@ -3,9 +3,6 @@ // IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JS_IR_ES6 // IGNORE_BACKEND: NATIVE -// IGNORE_BACKEND_K2: JVM_IR -// K2 status: caused by: java.lang.IllegalStateException: Should not be here! -// It will be fixed after merging of MPP branch // MODULE: lib-common // FILE: common.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.fir.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.fir.kt index 700ff3878f4..704b33d0aee 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.fir.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.fir.kt @@ -4,6 +4,7 @@ import kotlin.reflect.KProperty fun lazy(initializer: () -> T): Lazy = TODO() + interface Lazy { operator fun getValue(thisRef: Any?, property: KProperty<*>): T = TODO() } @@ -36,6 +37,8 @@ expect object OuterObject { object NestedObject } +fun TODO(): Nothing = null!! + // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt index 6d0990b37d8..b2970508cc6 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.kt @@ -3,9 +3,10 @@ // FILE: common.kt import kotlin.reflect.KProperty -fun lazy(initializer: () -> T): Lazy = TODO() +fun lazy(initializer: () -> T): Lazy = TODO() + interface Lazy { - operator fun getValue(thisRef: Any?, property: KProperty<*>): T = TODO() + operator fun getValue(thisRef: Any?, property: KProperty<*>): T = TODO() } expect class OuterClass { @@ -36,6 +37,8 @@ expect object OuterObject { object NestedObject } +fun TODO(): Nothing = null!! + // MODULE: m2-jvm()()(m1-common) // FILE: jvm.kt diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt index 0b4474f1177..75f56d13cb9 100644 --- a/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/nestedClasses.txt @@ -1,6 +1,7 @@ // -- Module: -- package +public fun TODO(): kotlin.Nothing public fun lazy(/*0*/ initializer: () -> T): Lazy public interface Lazy { @@ -79,6 +80,7 @@ public expect object OuterObject { // -- Module: -- package +public fun TODO(): kotlin.Nothing public fun lazy(/*0*/ initializer: () -> T): Lazy public interface Lazy { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index ec208690982..07738768e01 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -31689,6 +31689,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { @TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule") @TestDataPath("$PROJECT_ROOT") public class MultiModule { + @Test + @TestMetadata("accessToLocalClassFromBackend.kt") + public void testAccessToLocalClassFromBackend() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/accessToLocalClassFromBackend.kt"); + } + @Test public void testAllFilesPresentInMultiModule() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/multiModule"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); @@ -31717,6 +31723,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testExpectActualSimple() throws Exception { runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimple.kt"); } + + @Test + @TestMetadata("expectInterfaceInSupertypes.kt") + public void testExpectInterfaceInSupertypes() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectInterfaceInSupertypes.kt"); + } + + @Test + @TestMetadata("fakeOverridesInPlatformModule.kt") + public void testFakeOverridesInPlatformModule() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/fakeOverridesInPlatformModule.kt"); + } + + @Test + @TestMetadata("getRidOfDoubleBindingInFir2IrLazyProperty.kt") + public void testGetRidOfDoubleBindingInFir2IrLazyProperty() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt"); + } } } diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 1e68bca99ee..eccd08dc411 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -32691,6 +32691,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes @TestMetadata("compiler/testData/codegen/box/multiplatform/multiModule") @TestDataPath("$PROJECT_ROOT") public class MultiModule { + @Test + @TestMetadata("accessToLocalClassFromBackend.kt") + public void testAccessToLocalClassFromBackend() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/accessToLocalClassFromBackend.kt"); + } + @Test public void testAllFilesPresentInMultiModule() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/multiModule"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); @@ -32750,6 +32756,24 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualTypealias.kt"); } + @Test + @TestMetadata("expectInterfaceInSupertypes.kt") + public void testExpectInterfaceInSupertypes() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectInterfaceInSupertypes.kt"); + } + + @Test + @TestMetadata("fakeOverridesInPlatformModule.kt") + public void testFakeOverridesInPlatformModule() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/fakeOverridesInPlatformModule.kt"); + } + + @Test + @TestMetadata("getRidOfDoubleBindingInFir2IrLazyProperty.kt") + public void testGetRidOfDoubleBindingInFir2IrLazyProperty() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt"); + } + @Test @TestMetadata("kt-51753-1.kt") public void testKt_51753_1() throws Exception { diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt index e4413cd8f91..8554b781ec4 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/ir/JvmIrBackendFacade.kt @@ -7,8 +7,10 @@ package org.jetbrains.kotlin.test.backend.ir import org.jetbrains.kotlin.KtPsiSourceFile import org.jetbrains.kotlin.backend.common.BackendException +import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer import org.jetbrains.kotlin.backend.jvm.MultifileFacadeFileEntry import org.jetbrains.kotlin.backend.jvm.lower.getFileClassInfoFromIrFile +import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.ir.PsiIrFileEntry import org.jetbrains.kotlin.ir.declarations.IrFile @@ -16,10 +18,7 @@ import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.backend.classic.JavaCompilerFacade import org.jetbrains.kotlin.test.directives.CodegenTestDirectives -import org.jetbrains.kotlin.test.model.ArtifactKinds -import org.jetbrains.kotlin.test.model.BinaryArtifacts -import org.jetbrains.kotlin.test.model.SourceFileInfo -import org.jetbrains.kotlin.test.model.TestModule +import org.jetbrains.kotlin.test.model.* import org.jetbrains.kotlin.test.services.TestServices import org.jetbrains.kotlin.test.services.compilerConfigurationProvider @@ -35,9 +34,14 @@ class JvmIrBackendFacade( require(inputArtifact is IrBackendInput.JvmIrBackendInput) { "JvmIrBackendFacade expects IrBackendInput.JvmIrBackendInput as input" } + + if (module.useIrActualizer()) { + IrActualizer.actualize(inputArtifact.backendInput.irModuleFragment, inputArtifact.dependentInputs.map { it.irModuleFragment }) + } + val state = inputArtifact.state try { - inputArtifact.codegenFactory.generateModule(state, inputArtifact.backendInput.last()) + inputArtifact.codegenFactory.generateModule(state, inputArtifact.backendInput) } catch (e: BackendException) { if (CodegenTestDirectives.IGNORE_ERRORS in module.directives) { return null @@ -79,4 +83,8 @@ class JvmIrBackendFacade( } ) } + + private fun TestModule.useIrActualizer(): Boolean { + return frontendKind == FrontendKinds.FIR && languageVersionSettings.supportsFeature(LanguageFeature.MultiPlatformProjects) + } } diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/Fir2IrResultsConverter.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/Fir2IrResultsConverter.kt index 765509de807..2390dc4a253 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/Fir2IrResultsConverter.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/Fir2IrResultsConverter.kt @@ -96,6 +96,8 @@ class Fir2IrResultsConverter( } } + val mainModuleComponents = componentsMap[module.name]!! + val codegenFactory = JvmIrCodegenFactory(configuration, phaseConfig) val generationState = GenerationState.Builder( project, ClassBuilderFactories.TEST, @@ -103,7 +105,7 @@ class Fir2IrResultsConverter( ).isIrBackend( true ).jvmBackendClassResolver( - FirJvmBackendClassResolver(componentsMap[module.name]!!) + FirJvmBackendClassResolver(mainModuleComponents) ).build() return IrBackendInput.JvmIrBackendInput( diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt index 3fb20c8d918..f5e4aa341e4 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirFrontendFacade.kt @@ -100,8 +100,7 @@ open class FirFrontendFacade( testModule, moduleDataMap[testModule]!!, targetPlatform, - projectEnvironment, - isMppSupported = isMppSupported + projectEnvironment ) ) } @@ -258,8 +257,7 @@ open class FirFrontendFacade( module: TestModule, moduleData: FirModuleData, targetPlatform: TargetPlatform, - projectEnvironment: AbstractProjectEnvironment?, - isMppSupported: Boolean, + projectEnvironment: AbstractProjectEnvironment? ): FirOutputPartForDependsOnModule { val compilerConfigurationProvider = testServices.compilerConfigurationProvider val moduleInfoProvider = testServices.firModuleInfoProvider @@ -305,7 +303,7 @@ open class FirFrontendFacade( IrGenerationExtension.getInstances(project), lightTreeEnabled, enablePluginPhases, - generateSignatures = module.targetBackend == TargetBackend.JVM_IR_SERIALIZE || isMppSupported, + generateSignatures = module.targetBackend == TargetBackend.JVM_IR_SERIALIZE ) val firFiles = firAnalyzerFacade.runResolution() val filesMap = firFiles.mapNotNull { firFile -> diff --git a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java index 193635e67d2..9e2f584a511 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/cli/CliTestGenerated.java @@ -452,6 +452,11 @@ public class CliTestGenerated extends AbstractCliTest { runTest("compiler/testData/cli/jvm/firMultiplatformCompilationWithError.args"); } + @TestMetadata("firMultiplatformCompilationWithLightTreeWithoutErrors.args") + public void testFirMultiplatformCompilationWithLightTreeWithoutErrors() throws Exception { + runTest("compiler/testData/cli/jvm/firMultiplatformCompilationWithLightTreeWithoutErrors.args"); + } + @TestMetadata("firMultiplatformCompilationWithoutErrors.args") public void testFirMultiplatformCompilationWithoutErrors() throws Exception { runTest("compiler/testData/cli/jvm/firMultiplatformCompilationWithoutErrors.args"); diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index e412ad2e1ab..b23eee7b6a3 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -26974,6 +26974,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } + @TestMetadata("accessToLocalClassFromBackend.kt") + public void testAccessToLocalClassFromBackend() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/accessToLocalClassFromBackend.kt"); + } + public void testAllFilesPresentInMultiModule() throws Exception { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/multiplatform/multiModule"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } @@ -26997,6 +27002,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes public void testExpectActualSimple() throws Exception { runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectActualSimple.kt"); } + + @TestMetadata("expectInterfaceInSupertypes.kt") + public void testExpectInterfaceInSupertypes() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/expectInterfaceInSupertypes.kt"); + } + + @TestMetadata("fakeOverridesInPlatformModule.kt") + public void testFakeOverridesInPlatformModule() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/fakeOverridesInPlatformModule.kt"); + } + + @TestMetadata("getRidOfDoubleBindingInFir2IrLazyProperty.kt") + public void testGetRidOfDoubleBindingInFir2IrLazyProperty() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt"); + } } }