[IR] Implement IR actualizer and use it for K2 test and CLI scenario
Implement calculateExpectActualMap for Fir2IrComponents ^KT-51753 Fixed
This commit is contained in:
committed by
Space Team
parent
15ad9d134c
commit
8936220876
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IrGenerationExtension>,
|
||||
kotlinBuiltIns: KotlinBuiltIns
|
||||
kotlinBuiltIns: KotlinBuiltIns,
|
||||
dependentComponents: List<Fir2IrComponents>
|
||||
): 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
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -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 {
|
||||
|
||||
+232
@@ -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<IrModuleFragment>) {
|
||||
fun collect(): Map<IrSymbol, IrSymbol> {
|
||||
val result = mutableMapOf<IrSymbol, IrSymbol>()
|
||||
// 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<IrSymbol, IrSymbol>.appendExpectActualClassifiersMap(): Pair<Set<IrDeclaration>, Map<FqName, FqName>> {
|
||||
val actualClassifiers = mutableMapOf<FqName, IrSymbol>()
|
||||
// There is no list for builtins declarations, that's why they are being collected from typealiases
|
||||
val allActualDeclarations = mutableSetOf<IrDeclaration>()
|
||||
val typeAliasMap = mutableMapOf<FqName, FqName>() // 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<FqName, IrSymbol>,
|
||||
private val allActualClassifiers: MutableSet<IrDeclaration>,
|
||||
private val typeAliasMap: MutableMap<FqName, FqName>
|
||||
) : 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<IrSymbol, IrSymbol>,
|
||||
private val actualClassifiers: Map<FqName, IrSymbol>
|
||||
) : 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<IrSymbol, IrSymbol>.appendExpectActualCallablesMap(
|
||||
allActualDeclarations: Set<IrDeclaration>,
|
||||
typeAliasMap: Map<FqName, FqName>,
|
||||
dependentFragments: List<IrModuleFragment>
|
||||
) {
|
||||
val actualFunctions = mutableMapOf<CallableId, MutableList<IrFunction>>()
|
||||
val actualProperties = mutableMapOf<CallableId, IrProperty>()
|
||||
|
||||
collectActualCallables(actualFunctions, actualProperties, allActualDeclarations)
|
||||
val collector = CallablesLinkCollector(this, actualFunctions, actualProperties, typeAliasMap)
|
||||
dependentFragments.forEach { collector.visitModuleFragment(it) }
|
||||
}
|
||||
|
||||
private fun collectActualCallables(
|
||||
actualFunctions: MutableMap<CallableId, MutableList<IrFunction>>,
|
||||
actualProperties: MutableMap<CallableId, IrProperty>,
|
||||
allActualDeclarations: Set<IrDeclaration>
|
||||
) {
|
||||
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<IrSymbol, IrSymbol>,
|
||||
private val actualFunctions: MutableMap<CallableId, MutableList<IrFunction>>,
|
||||
private val actualProperties: MutableMap<CallableId, IrProperty>,
|
||||
private val typeAliasMap: Map<FqName, FqName>
|
||||
) : 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+161
@@ -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<IrSymbol, IrSymbol>) {
|
||||
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 <reified S : IrSymbol> 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)
|
||||
}
|
||||
+40
@@ -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<IrModuleFragment>) {
|
||||
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<IrModuleFragment>) {
|
||||
for (fragment in dependentFragments) {
|
||||
for (file in fragment.files) {
|
||||
file.declarations.removeAll { it.isProperExpect }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMissingFakeOverrides(expectActualMap: Map<IrSymbol, IrSymbol>, dependentFragments: List<IrModuleFragment>) {
|
||||
MissingFakeOverridesAdder(expectActualMap).apply { dependentFragments.forEach { visitModuleFragment(it) } }
|
||||
}
|
||||
|
||||
private fun linkExpectToActual(expectActualMap: Map<IrSymbol, IrSymbol>, dependentFragments: List<IrModuleFragment>) {
|
||||
ExpectActualLinker(expectActualMap).apply { dependentFragments.forEach { actualize(it) } }
|
||||
}
|
||||
|
||||
private fun mergeIrFragments(mainFragment: IrModuleFragment, dependentFragments: List<IrModuleFragment>) {
|
||||
mainFragment.files.addAll(dependentFragments.flatMap { it.files })
|
||||
}
|
||||
}
|
||||
+40
@@ -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<IrSymbol, IrSymbol>
|
||||
): 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")
|
||||
}
|
||||
+148
@@ -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<IrSymbol, IrSymbol>) : 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<IrSymbol, IrSymbol>) {
|
||||
val members by lazy(LazyThreadSafetyMode.NONE) {
|
||||
declaration.declarations.filter { !it.isBuiltinMember() }.filterIsInstance<IrDeclarationWithName>()
|
||||
.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
|
||||
}
|
||||
-1
@@ -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 ||
|
||||
|
||||
@@ -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
|
||||
|
||||
+11
@@ -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
|
||||
+14
@@ -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
|
||||
@@ -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
|
||||
|
||||
+18
@@ -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()
|
||||
+20
@@ -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" }
|
||||
+22
@@ -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()
|
||||
+53
@@ -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"
|
||||
+30
@@ -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")
|
||||
+36
@@ -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"
|
||||
+25
@@ -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")
|
||||
+25
@@ -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()
|
||||
+24
@@ -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()
|
||||
+40
@@ -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"
|
||||
}
|
||||
}
|
||||
+22
@@ -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()
|
||||
compiler/testData/codegen/box/multiplatform/multiModule/getRidOfDoubleBindingInFir2IrLazyProperty.kt
Vendored
+25
@@ -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()
|
||||
@@ -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)"
|
||||
@@ -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<T> {
|
||||
var value: T
|
||||
|
||||
inline operator fun getValue(thisRef: Any?, property: KProperty<*>): T
|
||||
|
||||
inline operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T)
|
||||
}
|
||||
|
||||
expect fun <T> atomic(initial: T): AtomicRef<T>
|
||||
|
||||
// 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<T> 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 <T> atomic(initial: T): AtomicRef<T> = AtomicRef(initial)
|
||||
|
||||
// FILE: jvm/box.kt
|
||||
|
||||
fun box(): String {
|
||||
val s = topLevelDelegatedPropertyRef
|
||||
return if (s == "A") "OK" else "FAIL($s)"
|
||||
}
|
||||
-3
@@ -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
|
||||
|
||||
+3
@@ -4,6 +4,7 @@
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
fun <T> lazy(initializer: () -> T): Lazy<T> = TODO()
|
||||
|
||||
interface Lazy<out T> {
|
||||
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
|
||||
|
||||
|
||||
+5
-2
@@ -3,9 +3,10 @@
|
||||
// FILE: common.kt
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
fun <T> lazy(initializer: () -> T): Lazy<T> = <!UNRESOLVED_REFERENCE!>TODO<!>()
|
||||
fun <T> lazy(initializer: () -> T): Lazy<T> = TODO()
|
||||
|
||||
interface Lazy<out T> {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = <!UNRESOLVED_REFERENCE!>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
|
||||
|
||||
|
||||
+2
@@ -1,6 +1,7 @@
|
||||
// -- Module: <m1-common> --
|
||||
package
|
||||
|
||||
public fun TODO(): kotlin.Nothing
|
||||
public fun </*0*/ T> lazy(/*0*/ initializer: () -> T): Lazy<T>
|
||||
|
||||
public interface Lazy</*0*/ out T> {
|
||||
@@ -79,6 +80,7 @@ public expect object OuterObject {
|
||||
// -- Module: <m2-jvm> --
|
||||
package
|
||||
|
||||
public fun TODO(): kotlin.Nothing
|
||||
public fun </*0*/ T> lazy(/*0*/ initializer: () -> T): Lazy<T>
|
||||
|
||||
public interface Lazy</*0*/ out T> {
|
||||
|
||||
+24
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -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 {
|
||||
|
||||
+13
-5
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -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(
|
||||
|
||||
+3
-5
@@ -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 ->
|
||||
|
||||
@@ -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");
|
||||
|
||||
+20
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user