[IR] Use common expect/actual matching algorithm in IR actualizer

^KT-58578 Fixed
This commit is contained in:
Dmitriy Novozhilov
2023-06-06 17:27:03 +03:00
committed by Space Team
parent 62534f43c9
commit ba41e8ec38
52 changed files with 1130 additions and 440 deletions
@@ -14,7 +14,6 @@ import org.jetbrains.kotlin.cli.common.fir.FirDiagnosticsCompilerResultsReporter
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.constant.EvaluatedConstTracker
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
@@ -36,6 +35,7 @@ import org.jetbrains.kotlin.incremental.js.IncrementalDataProvider
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerDesc
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsManglerIr
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices
@@ -217,6 +217,7 @@ fun transformFirToIr(
visibilityConverter = Fir2IrVisibilityConverter.Default,
kotlinBuiltIns = builtInsModule ?: DefaultBuiltIns.Instance,
diagnosticReporter = diagnosticsReporter,
actualizerTypeContextProvider = ::IrTypeSystemContextImpl,
fir2IrResultPostCompute = {
(this.irModuleFragment.descriptor as? FirModuleDescriptor)?.let { it.allDependencyModules = librariesDescriptors }
}
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.pipeline
import org.jetbrains.kotlin.backend.common.actualizer.IrActualizedResult
import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext
import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -20,10 +21,12 @@ import org.jetbrains.kotlin.fir.backend.jvm.FirJvmVisibilityConverter
import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.resolve.ScopeSession
import org.jetbrains.kotlin.fir.signaturer.FirMangler
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmDescriptorMangler
import org.jetbrains.kotlin.ir.backend.jvm.serialization.JvmIrMangler
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.IdSignatureComposer
import org.jetbrains.kotlin.ir.util.KotlinMangler
@@ -57,6 +60,7 @@ fun FirResult.convertToIrAndActualizeForJvm(
visibilityConverter = FirJvmVisibilityConverter,
diagnosticReporter = diagnosticReporter,
kotlinBuiltIns = DefaultBuiltIns.Instance,
actualizerTypeContextProvider = ::JvmIrTypeSystemContext,
)
fun signatureComposerForJvmFir2Ir(generateSignatures: Boolean): IdSignatureComposer {
@@ -78,6 +82,7 @@ fun FirResult.convertToIrAndActualize(
visibilityConverter: Fir2IrVisibilityConverter,
kotlinBuiltIns: KotlinBuiltIns,
diagnosticReporter: DiagnosticReporter,
actualizerTypeContextProvider: (IrBuiltIns) -> IrTypeSystemContext,
fir2IrResultPostCompute: Fir2IrResult.() -> Unit = {},
): Fir2IrActualizedResult {
val fir2IrResult: Fir2IrResult
@@ -140,6 +145,7 @@ fun FirResult.convertToIrAndActualize(
fir2IrResult.irModuleFragment,
commonIrOutputs.map { it.irModuleFragment },
diagnosticReporter,
actualizerTypeContextProvider(fir2IrResult.irModuleFragment.irBuiltins),
fir2IrConfiguration.languageVersionSettings
)
}
@@ -70,6 +70,13 @@ open class KtDiagnosticReporterWithContext(
}
}
fun <A : Any, B : Any, C : Any> report(factory: KtDiagnosticFactory3<A, B, C>, a: A, b: B, c: C) {
sourceElement?.let {
reportOn(it, factory, a, b, c, this)
checkAndCommitReportsOn(it, this)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DiagnosticContextImpl) return false
@@ -6,18 +6,24 @@
package org.jetbrains.kotlin.backend.common
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.backend.common.BackendDiagnosticRenderers.INCOMPATIBILITY
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.error0
import org.jetbrains.kotlin.diagnostics.error2
import org.jetbrains.kotlin.diagnostics.error3
import org.jetbrains.kotlin.diagnostics.rendering.BaseDiagnosticRendererFactory
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers.STRING
import org.jetbrains.kotlin.diagnostics.rendering.Renderer
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.MODULE_WITH_PLATFORM
import org.jetbrains.kotlin.diagnostics.rendering.RootDiagnosticRendererFactory
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
object CommonBackendErrors {
val NO_ACTUAL_FOR_EXPECT by error2<PsiElement, String, ModuleDescriptor>()
val MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED by error2<PsiElement, String, String>()
val MANY_IMPL_MEMBER_NOT_IMPLEMENTED by error2<PsiElement, String, String>()
val INCOMPATIBLE_MATCHING by error3<PsiElement, String, String, ExpectActualCompatibility.Incompatible<*>>()
init {
RootDiagnosticRendererFactory.registerFactory(KtDefaultCommonBackendErrorMessages)
@@ -44,5 +50,18 @@ object KtDefaultCommonBackendErrorMessages : BaseDiagnosticRendererFactory() {
STRING,
STRING,
)
map.put(
CommonBackendErrors.INCOMPATIBLE_MATCHING,
"Expect declaration `{0}` doesn''t match actual `{1}` because {2}",
STRING,
STRING,
INCOMPATIBILITY
)
}
}
object BackendDiagnosticRenderers {
val INCOMPATIBILITY = Renderer<ExpectActualCompatibility.Incompatible<*>> {
it.reason ?: "<unknown>"
}
}
@@ -8,13 +8,18 @@ package org.jetbrains.kotlin.backend.common.actualizer
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
import org.jetbrains.kotlin.backend.common.CommonBackendErrors
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
/**
* It adds fake overrides to non-expect classes inside common or multi-platform module,
@@ -27,9 +32,11 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
*/
internal class ActualFakeOverridesAdder(
private val expectActualMap: Map<IrSymbol, IrSymbol>,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
private val expectToActualClassMap: Map<ClassId, IrClassSymbol>,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
private val typeSystemContext: IrTypeSystemContext
) : IrElementVisitorVoid {
private val missingActualMembersMap = mutableMapOf<IrClass, MutableMap<String, MutableList<IrDeclaration>>>()
private val missingActualMembersMap = mutableMapOf<IrClass, FakeOverrideInfo>()
override fun visitClass(declaration: IrClass) {
extractMissingActualMembersFromSupertypes(declaration)
@@ -40,20 +47,20 @@ internal class ActualFakeOverridesAdder(
element.acceptChildrenVoid(this)
}
private fun extractMissingActualMembersFromSupertypes(klass: IrClass): Map<String, MutableList<IrDeclaration>> {
private fun extractMissingActualMembersFromSupertypes(klass: IrClass): FakeOverrideInfo {
missingActualMembersMap[klass]?.let { return it }
val missingActualMembers = mutableMapOf<String, MutableList<IrDeclaration>>()
val missingActualMembers = FakeOverrideInfo()
missingActualMembersMap[klass] = missingActualMembers
// New members from supertypes are only relevant for not expect (ordinary) classes
// New members from the current class are only relevant for actualized expect classes
val processedMembers = mutableMapOf<String, MutableList<IrDeclaration>>()
val processedMembers = FakeOverrideInfo()
for (superType in klass.superTypes) {
val membersFromSupertype = extractMissingActualMembersFromSupertypes(superType.classifierOrFail.owner as IrClass)
if (!klass.isExpect) {
missingActualMembers.appendMissingMembersToNotExpectClass(klass, membersFromSupertype, processedMembers)
appendMissingMembersToNotExpectClass(missingActualMembers, klass, membersFromSupertype.allSymbols(), processedMembers)
}
}
@@ -64,22 +71,35 @@ internal class ActualFakeOverridesAdder(
return missingActualMembers
}
private fun MutableMap<String, MutableList<IrDeclaration>>.appendMissingMembersToNotExpectClass(
private fun appendMissingMembersToNotExpectClass(
fakeOverrideInfo: FakeOverrideInfo,
klass: IrClass,
membersFromSupertype: Map<String, MutableList<IrDeclaration>>,
processedMembers: MutableMap<String, MutableList<IrDeclaration>>
membersFromSupertype: List<IrSymbol>,
processedMembers: FakeOverrideInfo
) {
for (memberFromSupertype in membersFromSupertype.flatMap { it.value }) {
for (symbolFromSupertype in membersFromSupertype) {
val memberFromSupertype = symbolFromSupertype.owner as IrDeclaration
val newMember = createFakeOverrideMember(listOf(memberFromSupertype), klass)
val name = newMember.getFunctionOrPropertyName()
if (getMatches(name, newMember, expectActualMap).isEmpty()) {
val matchingFakeOverrides = collectActualCallablesMatchingToSpecificExpect(
newMember.symbol,
fakeOverrideInfo.getMembersForActual(newMember),
expectToActualClassMap,
typeSystemContext
)
if (matchingFakeOverrides.isEmpty()) {
processedMembers.addMember(memberFromSupertype as IrOverridableDeclaration<*>)
addMember(newMember)
fakeOverrideInfo.addMember(newMember)
klass.addMember(newMember)
} else {
val baseMember = processedMembers.getMatches(name, newMember, expectActualMap).single()
val baseMembers = collectActualCallablesMatchingToSpecificExpect(
newMember.symbol,
processedMembers.getMembersForActual(newMember),
expectToActualClassMap,
typeSystemContext
)
val errorFactory =
if ((baseMember.parent as IrClass).isInterface && (memberFromSupertype.parent as IrClass).isInterface)
if (baseMembers.all { ((it.owner as IrDeclaration).parent as IrClass).isInterface } && (memberFromSupertype.parent as IrClass).isInterface)
CommonBackendErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED
else
CommonBackendErrors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED
@@ -92,7 +112,7 @@ internal class ActualFakeOverridesAdder(
}
}
private fun MutableMap<String, MutableList<IrDeclaration>>.appendMissingMembersFromActualizedExpectClass(
private fun FakeOverrideInfo.appendMissingMembersFromActualizedExpectClass(
expectClass: IrClass,
actualClass: IrClass,
) {
@@ -110,11 +130,31 @@ internal class ActualFakeOverridesAdder(
}
}
private fun MutableMap<String, MutableList<IrDeclaration>>.addMember(member: IrOverridableDeclaration<*>) {
getOrPut(member.getFunctionOrPropertyName()) { mutableListOf() }.add(member)
}
private class FakeOverrideInfo {
val functionsByName: MutableMap<Name, MutableList<IrSymbol>> = mutableMapOf()
val propertiesByName: MutableMap<Name, MutableList<IrSymbol>> = mutableMapOf()
private fun IrOverridableDeclaration<*>.getFunctionOrPropertyName(): String {
return (this as IrDeclarationWithName).name.asString() + (if (this is IrSimpleFunction) "()" else "")
fun allSymbols(): List<IrSymbol> {
return buildList {
functionsByName.values.flatMapTo(this) { it }
propertiesByName.values.flatMapTo(this) { it }
}
}
private fun getCorrespondingMap(member: IrOverridableDeclaration<*>): MutableMap<Name, MutableList<IrSymbol>> {
return when (member) {
is IrFunction -> functionsByName
is IrProperty -> propertiesByName
else -> error("Unsupported declaration type: $member")
}
}
fun addMember(member: IrOverridableDeclaration<*>) {
getCorrespondingMap(member).getOrPut(member.name) { mutableListOf() } += member.symbol
}
fun getMembersForActual(actualMember: IrOverridableDeclaration<*>): List<IrSymbol> {
return getCorrespondingMap(actualMember)[actualMember.name].orEmpty()
}
}
}
}
@@ -8,66 +8,97 @@ package org.jetbrains.kotlin.backend.common.actualizer
import org.jetbrains.kotlin.KtDiagnosticReporterWithImplicitIrBasedContext
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.symbols.*
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.ir.util.callableId
import org.jetbrains.kotlin.ir.util.classIdOrFail
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.mpp.DeclarationSymbolMarker
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualCompatibilityChecker
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
/**
* This class is used to collect mapping between all expect and actuals declarations, which are declared in passed module fragments
* It does not process any declarations which may appear after actualization of supertypes (like fake-overrides in classes with
* expect supertypes)
*
* The main method of this class, `collect` returns a pair of two values:
* - `expectToActualMap` is the main storage of all mapped declarations. Key is symbol of expect declaration and the value is symbol of
* corresponding actual declaration
* - `classActualizationInfo` is a storage which keeps information about types (class) actualization, which can be used later for type
* refinement if needed
*
* If some declarations didn't match (or there was missing/actual) then corresponding declarations won't be stored in `expectToActualMap`.
* Instead of that an error will be reported to `diagnosticReporter`
*/
internal class ExpectActualCollector(
private val mainFragment: IrModuleFragment,
private val dependentFragments: List<IrModuleFragment>,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
private val typeSystemContext: IrTypeSystemContext,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
) {
fun collect(): MutableMap<IrSymbol, IrSymbol> {
data class Result(val expectToActualMap: MutableMap<IrSymbol, IrSymbol>, val classActualizationInfo: ClassActualizationInfo)
fun collect(): Result {
val result = mutableMapOf<IrSymbol, IrSymbol>()
// Collect and link classes at first to make it possible to expand type aliases on the members linking
val (actualMembers, expectActualTypeAliasMap) = result.appendExpectActualClassMap()
result.appendExpectActualMemberMap(actualMembers, expectActualTypeAliasMap)
return result
val actualDeclarations = collectActualDeclarations()
matchAllExpectDeclarations(result, actualDeclarations)
return Result(result, actualDeclarations)
}
private fun MutableMap<IrSymbol, IrSymbol>.appendExpectActualClassMap(): Pair<List<IrDeclarationBase>, Map<FqName, FqName>> {
val actualClasses = mutableMapOf<String, IrClassSymbol>()
// There is no list for builtins declarations; that's why they are being collected from typealiases
val actualMembers = mutableListOf<IrDeclarationBase>()
val expectActualTypeAliasMap = mutableMapOf<FqName, FqName>() // It's used to link members from expect class that have typealias actual
private fun collectActualDeclarations(): ClassActualizationInfo {
val fragmentsWithActuals = dependentFragments.drop(1) + mainFragment
val actualClassesAndMembersCollector = ActualClassesAndMembersCollector(actualClasses, actualMembers, expectActualTypeAliasMap)
fragmentsWithActuals.forEach { actualClassesAndMembersCollector.collect(it) }
val linkCollector = ClassLinksCollector(this, actualClasses, expectActualTypeAliasMap, diagnosticsReporter)
dependentFragments.forEach { linkCollector.visitModuleFragment(it) }
return actualMembers to expectActualTypeAliasMap
return ActualDeclarationsCollector.collectActualsFromFragments(fragmentsWithActuals)
}
private fun MutableMap<IrSymbol, IrSymbol>.appendExpectActualMemberMap(
actualMembers: List<IrDeclarationBase>,
expectActualTypeAliasMap: Map<FqName, FqName>
private fun matchAllExpectDeclarations(
destination: MutableMap<IrSymbol, IrSymbol>,
classActualizationInfo: ClassActualizationInfo,
) {
val actualMembersMap = mutableMapOf<String, MutableList<IrDeclarationBase>>()
for (actualMember in actualMembers) {
actualMembersMap.getOrPut(generateIrElementFullNameFromExpect(actualMember, expectActualTypeAliasMap)) { mutableListOf() }
.add(actualMember)
}
val collector = MemberLinksCollector(this, actualMembersMap, expectActualTypeAliasMap, diagnosticsReporter)
dependentFragments.forEach { collector.visitModuleFragment(it) }
val linkCollector = ExpectActualLinkCollector(destination, classActualizationInfo, typeSystemContext, diagnosticsReporter)
dependentFragments.forEach { linkCollector.visitModuleFragment(it) }
}
}
private class ActualClassesAndMembersCollector(
private val actualClasses: MutableMap<String, IrClassSymbol>,
private val actualMembers: MutableList<IrDeclarationBase>,
private val expectActualTypeAliasMap: MutableMap<FqName, FqName>
internal data class ClassActualizationInfo(
// mapping from classId of actual class/typealias to itself/typealias expansion
val actualClasses: Map<ClassId, IrClassSymbol>,
// mapping from classId to actual typealias
val actualTypeAliases: Map<ClassId, IrTypeAliasSymbol>,
val actualTopLevels: Map<CallableId, List<IrDeclarationWithName>>,
) {
fun getActualWithoutExpansion(classId: ClassId): IrSymbol? {
return actualTypeAliases[classId] ?: actualClasses[classId]
}
}
private class ActualDeclarationsCollector {
companion object {
fun collectActualsFromFragments(fragments: List<IrModuleFragment>): ClassActualizationInfo {
val collector = ActualDeclarationsCollector()
for (fragment in fragments) {
collector.collect(fragment)
}
return ClassActualizationInfo(
collector.actualClasses,
collector.actualTypeAliasesWithoutExpansion,
collector.actualTopLevels
)
}
}
private val actualClasses: MutableMap<ClassId, IrClassSymbol> = mutableMapOf()
private val actualTypeAliasesWithoutExpansion: MutableMap<ClassId, IrTypeAliasSymbol> = mutableMapOf()
private val actualTopLevels: MutableMap<CallableId, MutableList<IrDeclarationWithName>> = mutableMapOf()
private val visitedActualClasses = mutableSetOf<IrClass>()
fun collect(element: IrElement) {
private fun collect(element: IrElement) {
when (element) {
is IrModuleFragment -> {
for (file in element.files) {
@@ -77,16 +108,18 @@ private class ActualClassesAndMembersCollector(
is IrTypeAlias -> {
if (!element.isActual) return
val classId = element.classIdOrFail
val expandedTypeSymbol = element.expandedType.classifierOrFail as IrClassSymbol
addActualClass(element, expandedTypeSymbol)
collect(expandedTypeSymbol.owner)
expectActualTypeAliasMap[element.kotlinFqName] = expandedTypeSymbol.owner.kotlinFqName
actualClasses[classId] = expandedTypeSymbol
actualTypeAliasesWithoutExpansion[classId] = element.symbol
collect(expandedTypeSymbol.owner)
}
is IrClass -> {
if (element.isExpect || !visitedActualClasses.add(element)) return
addActualClass(element, element.symbol)
actualClasses[element.classIdOrFail] = element.symbol
for (declaration in element.declarations) {
collect(declaration)
}
@@ -97,101 +130,99 @@ private class ActualClassesAndMembersCollector(
}
}
is IrEnumEntry -> {
actualMembers.add(element) // If enum entry is located inside expect enum, then this code is not executed
recordActualCallable(element, element.callableId) // If enum entry is located inside expect enum, then this code is not executed
}
is IrProperty -> {
if (element.isExpect) return
actualMembers.add(element)
recordActualCallable(element, element.callableId)
}
is IrFunction -> {
if (element.isExpect) return
actualMembers.add(element)
recordActualCallable(element, element.callableId)
}
}
}
private fun addActualClass(classOrTypeAlias: IrElement, classSymbol: IrClassSymbol) {
actualClasses[generateActualIrClassOrTypeAliasFullName(classOrTypeAlias)] = classSymbol
private fun recordActualCallable(callableDeclaration: IrDeclarationWithName, callableId: CallableId) {
if (callableId.classId == null) {
actualTopLevels
.getOrPut(callableId) { mutableListOf() }
.add(callableDeclaration)
}
}
}
private class ClassLinksCollector(
private val expectActualMap: MutableMap<IrSymbol, IrSymbol>,
private val actualClasses: Map<String, IrClassSymbol>,
private val expectActualTypeAliasMap: Map<FqName, FqName>,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
private class ExpectActualLinkCollector(
private val destination: MutableMap<IrSymbol, IrSymbol>,
private val classActualizationInfo: ClassActualizationInfo,
typeSystemContext: IrTypeSystemContext,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext,
) : IrElementVisitorVoid {
private val context = MatchingContext(typeSystemContext)
override fun visitFunction(declaration: IrFunction) {
if (declaration.isExpect) {
matchExpectCallable(declaration, declaration.callableId)
}
}
override fun visitProperty(declaration: IrProperty) {
if (declaration.isExpect) {
matchExpectCallable(declaration, declaration.callableId)
}
}
private fun matchExpectCallable(declaration: IrDeclarationWithName, callableId: CallableId) {
val actualSymbols = classActualizationInfo.actualTopLevels[callableId]?.map { it.symbol }.orEmpty()
matchExpectDeclaration(declaration.symbol, actualSymbols)
}
override fun visitClass(declaration: IrClass) {
if (!declaration.isExpect) return
val classId = declaration.classIdOrFail
val expectClassSymbol = declaration.symbol
val actualClassLikeSymbol = classActualizationInfo.getActualWithoutExpansion(classId)
matchExpectDeclaration(expectClassSymbol, listOfNotNull(actualClassLikeSymbol))
}
val actualClassSymbol = actualClasses[generateIrElementFullNameFromExpect(declaration, expectActualTypeAliasMap)]
if (actualClassSymbol != null) {
expectActualMap[declaration.symbol] = actualClassSymbol
expectActualMap.appendTypeParametersMap(declaration, actualClassSymbol.owner)
} else if (!declaration.containsOptionalExpectation()) {
diagnosticsReporter.reportMissingActual(declaration)
}
visitElement(declaration)
private fun matchExpectDeclaration(expectSymbol: IrSymbol, actualSymbols: List<IrSymbol>) {
AbstractExpectActualCompatibilityChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol,
actualSymbols,
context
)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
}
private class MemberLinksCollector(
private val expectActualMap: MutableMap<IrSymbol, IrSymbol>,
private val actualMembers: Map<String, List<IrDeclarationBase>>,
private val typeAliasMap: Map<FqName, FqName>,
private val diagnosticsReporter: KtDiagnosticReporterWithImplicitIrBasedContext
) : IrElementVisitorVoid {
override fun visitFunction(declaration: IrFunction) {
if (declaration.isExpect) addLink(declaration)
}
private inner class MatchingContext(
typeSystemContext: IrTypeSystemContext,
) : IrExpectActualMatchingContext(typeSystemContext, classActualizationInfo.actualClasses) {
override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) {
destination[expectClassSymbol] = actualClassSymbol
recordActualForExpectDeclaration(expectClassSymbol, actualClassSymbol, destination)
}
override fun visitProperty(declaration: IrProperty) {
if (declaration.isExpect) addLink(declaration)
}
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
recordActualForExpectDeclaration(expectSymbol, actualSymbol, destination)
}
override fun visitEnumEntry(declaration: IrEnumEntry) {
if ((declaration.parent as IrClass).isExpect) addLink(declaration)
}
private fun addLink(expectDeclaration: IrDeclarationBase) {
val actualMemberMatches = actualMembers.getMatches(expectDeclaration, expectActualMap, typeAliasMap)
when {
actualMemberMatches.size == 1 -> {
expectActualMap.addLink(expectDeclaration, actualMemberMatches.single())
override fun onMismatchedMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>,
) {
require(expectSymbol is IrSymbol)
if (actualSymbolsByIncompatibility.isEmpty() && !expectSymbol.owner.containsOptionalExpectation()) {
diagnosticsReporter.reportMissingActual(expectSymbol)
}
actualMemberMatches.size > 1 -> {
// TODO: report AMBIGUOUS_ACTUALS here, see KT-57932
}
(expectDeclaration.parent as? IrClass)?.isExpect == false && expectDeclaration.isFakeOverride -> {
// Remaining expect fake override members from not expect classes will be actualized in FakeOverridesTransformer
// It occurs in a separated step because they require filled expectActualMap for members from expect classes
}
else -> {
if (shouldReportMissingActual(expectDeclaration)) {
diagnosticsReporter.reportMissingActual(expectDeclaration)
for ((incompatibility, actualMemberSymbols) in actualSymbolsByIncompatibility) {
for (actualSymbol in actualMemberSymbols) {
require(actualSymbol is IrSymbol)
diagnosticsReporter.reportIncompatibleExpectActual(expectSymbol, actualSymbol, incompatibility)
}
}
}
}
private fun shouldReportMissingActual(declaration: IrDeclarationBase): Boolean {
return !declaration.parent.containsOptionalExpectation() && !(declaration is IrConstructor && declaration.isPrimary)
}
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
}
fun MutableMap<IrSymbol, IrSymbol>.appendTypeParametersMap(
expectTypeParametersContainer: IrTypeParametersContainer,
actualTypeParametersContainer: IrTypeParametersContainer
) {
expectTypeParametersContainer.typeParameters.zip(actualTypeParametersContainer.typeParameters)
.forEach { (expectTypeParameter, actualTypeParameter) -> this[expectTypeParameter.symbol] = actualTypeParameter.symbol }
}
@@ -47,11 +47,11 @@ internal class FakeOverridesActualizer(private val expectActualMap: MutableMap<I
val actualizedOverrides = overriddenSymbols.map { (it.owner as IrDeclaration).actualize() }
val actualFakeOverride = createFakeOverrideMember(actualizedOverrides, parent as IrClass)
expectActualMap.addLink(this as IrDeclarationBase, actualFakeOverride)
recordActualForExpectDeclaration(this.symbol, actualFakeOverride.symbol, expectActualMap)
return actualFakeOverride
}
klass.declarations.transformInPlace { if (it.isExpect && it.isFakeOverride) it.actualize() else it }
}
}
}
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.DeepCopyTypeRemapper
data class IrActualizedResult(val actualizedExpectDeclarations: List<IrDeclaration>)
@@ -19,15 +20,17 @@ object IrActualizer {
mainFragment: IrModuleFragment,
dependentFragments: List<IrModuleFragment>,
diagnosticReporter: DiagnosticReporter,
typeSystemContext: IrTypeSystemContext,
languageVersionSettings: LanguageVersionSettings
): IrActualizedResult {
val ktDiagnosticReporter = KtDiagnosticReporterWithImplicitIrBasedContext(diagnosticReporter, languageVersionSettings)
// The ir modules processing is performed phase-to-phase:
// 1. Collect expect-actual links for classes and their members from dependent fragments
val expectActualMap = ExpectActualCollector(
val (expectActualMap, actualDeclarations) = ExpectActualCollector(
mainFragment,
dependentFragments,
typeSystemContext,
ktDiagnosticReporter
).collect()
@@ -44,7 +47,9 @@ object IrActualizer {
// taken from these non-expect classes actualized super classes.
ActualFakeOverridesAdder(
expectActualMap,
ktDiagnosticReporter
actualDeclarations.actualClasses,
ktDiagnosticReporter,
typeSystemContext
).apply { dependentFragments.forEach { visitModuleFragment(it) } }
// 5. Copy and actualize function parameter default values from expect functions
@@ -91,4 +96,4 @@ object IrActualizer {
private fun mergeIrFragments(mainFragment: IrModuleFragment, dependentFragments: List<IrModuleFragment>) {
mainFragment.files.addAll(0, dependentFragments.flatMap { it.files })
}
}
}
@@ -13,182 +13,89 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.declarations.buildProperty
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.IrDynamicType
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.types.IrTypeSystemContext
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualCompatibilityChecker
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.OptionalAnnotationUtil
import org.jetbrains.kotlin.utils.addToStdlib.runIf
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
private val FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME = StandardClassIds.Annotations.FlexibleNullability.asSingleFqName()
internal fun collectActualCallablesMatchingToSpecificExpect(
expectSymbol: IrSymbol,
actualSymbols: List<IrSymbol>,
expectToActualClassMap: Map<ClassId, IrClassSymbol>,
typeSystemContext: IrTypeSystemContext,
): List<IrSymbol> {
val baseExpectSymbol = expectSymbol
val matchingActuals = mutableListOf<IrSymbol>()
val context = object : IrExpectActualMatchingContext(typeSystemContext, expectToActualClassMap) {
override fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol) {
shouldNotBeCalled()
}
internal fun Map<String, List<IrDeclaration>>.getMatches(
expectDeclaration: IrDeclaration,
expectActualTypesMap: Map<IrSymbol, IrSymbol>,
expectActualTypeAliasMap: Map<FqName, FqName>
): List<IrDeclaration> =
getMatches(generateIrElementFullNameFromExpect(expectDeclaration, expectActualTypeAliasMap), expectDeclaration, expectActualTypesMap)
internal fun Map<String, List<IrDeclaration>>.getMatches(
mainSignature: String,
expectDeclaration: IrDeclaration,
expectActualTypesMap: Map<IrSymbol, IrSymbol>
): List<IrDeclaration> {
val members = this[mainSignature] ?: return emptyList()
return when (expectDeclaration) {
is IrFunction -> members.getMatches(expectDeclaration, expectActualTypesMap) { it as IrFunction }
is IrProperty -> members.getMatches(expectDeclaration, expectActualTypesMap) { (it as IrProperty).getter!! }
else -> members
override fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol) {
require(expectSymbol == baseExpectSymbol)
matchingActuals += actualSymbol
}
}
AbstractExpectActualCompatibilityChecker.matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectSymbol,
actualSymbols,
context
)
return matchingActuals
}
private inline fun List<IrDeclaration>.getMatches(
expect: IrDeclaration,
expectActualTypesMap: Map<IrSymbol, IrSymbol>,
functionExtractor: (IrDeclaration) -> IrFunction
): List<IrDeclaration> {
val expectFunction = functionExtractor(expect)
return filter { expectFunction.match(functionExtractor(it), expectActualTypesMap) }
}
private fun IrFunction.match(actualFunction: IrFunction, expectActualTypesMap: Map<IrSymbol, IrSymbol>): Boolean {
fun getActualizedTypeClassifierSymbol(
expectParameter: IrValueParameter,
localTypeParametersMap: Map<IrTypeParameterSymbol, IrTypeParameterSymbol>? = null
): IrSymbol {
return expectParameter.type.classifierOrFail.let {
val localMappedSymbol = if (localTypeParametersMap != null && it is IrTypeParameterSymbol) {
localTypeParametersMap[it]
} else {
null
}
localMappedSymbol ?: expectActualTypesMap[it] ?: it
}
}
fun checkParameter(
expectParameter: IrValueParameter?,
actualParameter: IrValueParameter?,
localTypeParametersMap: Map<IrTypeParameterSymbol, IrTypeParameterSymbol>
): Boolean {
if (expectParameter == null) {
return actualParameter == null
}
if (actualParameter == null) {
return false
}
if (expectParameter.type is IrDynamicType || actualParameter.type is IrDynamicType) {
return true
}
if (expectParameter.type.isNullable() != actualParameter.type.isNullable() &&
!expectParameter.type.annotations.hasAnnotation(FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME) &&
!actualParameter.type.annotations.hasAnnotation(FLEXIBLE_NULLABILITY_ANNOTATION_FQ_NAME)
) {
return false
}
if (getActualizedTypeClassifierSymbol(expectParameter, localTypeParametersMap) !=
getActualizedTypeClassifierSymbol(actualParameter)
) {
return false
}
return true
}
if (valueParameters.size != actualFunction.valueParameters.size || typeParameters.size != actualFunction.typeParameters.size) {
return false
}
val localTypeParametersMap = mutableMapOf<IrTypeParameterSymbol, IrTypeParameterSymbol>()
for ((expectTypeParameter, actualTypeParameter) in typeParameters.zip(actualFunction.typeParameters)) {
if (expectTypeParameter.name != actualTypeParameter.name) {
return false
}
localTypeParametersMap[expectTypeParameter.symbol] = actualTypeParameter.symbol
}
if (!checkParameter(extensionReceiverParameter, actualFunction.extensionReceiverParameter, localTypeParametersMap)) {
return false
}
for ((expectParameter, actualParameter) in valueParameters.zip(actualFunction.valueParameters)) {
if (!checkParameter(expectParameter, actualParameter, localTypeParametersMap)) {
return false
}
}
return true
}
internal fun generateActualIrClassOrTypeAliasFullName(declaration: IrElement) = generateIrElementFullNameFromExpect(declaration, emptyMap())
internal fun generateIrElementFullNameFromExpect(
declaration: IrElement,
expectActualTypeAliasMap: Map<FqName, FqName>
): String {
return buildString { appendElementFullName(declaration, this, expectActualTypeAliasMap) }
}
private fun appendElementFullName(
declaration: IrElement,
result: StringBuilder,
expectActualTypeAliasMap: Map<FqName, FqName>
internal fun recordActualForExpectDeclaration(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
destination: MutableMap<IrSymbol, IrSymbol>,
) {
if (declaration !is IrDeclarationBase) return
val parents = mutableListOf<String>()
var parent: IrDeclarationParent? = declaration.parent
while (parent != null) {
if (parent is IrDeclarationWithName) {
val parentParent = parent.parent
if (parentParent is IrClass) {
parents.add(parent.name.asString())
parent = parentParent
continue
}
val expectDeclaration = expectSymbol.owner as IrDeclarationBase
val actualDeclaration = actualSymbol.owner as IrDeclaration
val registeredActual = destination.put(expectSymbol, actualSymbol)
require(registeredActual == null || registeredActual == actualSymbol) {
"""
Expect symbol already has registered mapping
Expect declaration: ${expectDeclaration.render()}
Actual declaration: ${actualDeclaration.render()}
Already registered: ${registeredActual!!.owner.render()}
""".trimIndent()
}
if (expectDeclaration is IrTypeParametersContainer) {
recordTypeParametersMapping(destination, expectDeclaration, actualDeclaration as IrTypeParametersContainer)
}
if (expectDeclaration is IrProperty) {
val actualProperty = actualDeclaration as IrProperty
expectDeclaration.getter!!.let {
val getter = actualProperty.getter!!
destination[it.symbol] = getter.symbol
recordTypeParametersMapping(destination, it, getter)
}
val parentString = parent.kotlinFqName.let { (expectActualTypeAliasMap[it] ?: it).asString() }
if (parentString.isNotEmpty()) {
parents.add(parentString)
}
parent = null
}
if (parents.isNotEmpty()) {
result.append(parents.asReversed().joinToString(separator = "."))
result.append('.')
}
if (declaration is IrDeclarationWithName) {
result.append(declaration.name)
}
if (declaration is IrFunction) {
result.append("()")
expectDeclaration.setter?.symbol?.let { destination[it] = actualProperty.setter!!.symbol }
}
}
internal fun MutableMap<IrSymbol, IrSymbol>.addLink(expectMember: IrDeclarationBase, actualMember: IrDeclaration) {
this[expectMember.symbol] = actualMember.symbol
if (expectMember is IrProperty) {
val actualProperty = actualMember as IrProperty
expectMember.getter!!.let {
val getter = actualProperty.getter!!
this[it.symbol] = getter.symbol
this.appendTypeParametersMap(it, getter)
private fun recordTypeParametersMapping(
destination: MutableMap<IrSymbol, IrSymbol>,
expectTypeParametersContainer: IrTypeParametersContainer,
actualTypeParametersContainer: IrTypeParametersContainer
) {
expectTypeParametersContainer.typeParameters
.zip(actualTypeParametersContainer.typeParameters)
.forEach { (expectTypeParameter, actualTypeParameter) ->
destination[expectTypeParameter.symbol] = actualTypeParameter.symbol
}
expectMember.setter?.symbol?.let { this[it] = actualProperty.setter!!.symbol }
} else if (expectMember is IrFunction) {
this.appendTypeParametersMap(expectMember, actualMember as IrFunction)
}
}
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(expectSymbol: IrSymbol) {
reportMissingActual(expectSymbol.owner as IrDeclaration)
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
@@ -200,6 +107,21 @@ internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportMissingActual(
)
}
internal fun KtDiagnosticReporterWithImplicitIrBasedContext.reportIncompatibleExpectActual(
expectSymbol: IrSymbol,
actualSymbol: IrSymbol,
incompatibility: ExpectActualCompatibility.Incompatible<*>
) {
val expectDeclaration = expectSymbol.owner as IrDeclaration
val actualDeclaration = actualSymbol.owner as IrDeclaration
at(expectDeclaration).report(
CommonBackendErrors.INCOMPATIBLE_MATCHING,
expectDeclaration.getNameWithAssert().asString(),
actualDeclaration.getNameWithAssert().asString(),
incompatibility
)
}
internal fun IrElement.containsOptionalExpectation(): Boolean {
return this is IrClass &&
this.kind == ClassKind.ANNOTATION_CLASS &&
@@ -268,4 +190,4 @@ private fun createFakeOverrideFunction(
it.attributeOwnerId = it
it.correspondingPropertySymbol = correspondingPropertySymbol
}
}
}
@@ -0,0 +1,401 @@
/*
* 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.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrTypeBase
import org.jetbrains.kotlin.ir.types.impl.IrTypeProjectionImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
import org.jetbrains.kotlin.utils.addToStdlib.castAll
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled
internal abstract class IrExpectActualMatchingContext(
val typeContext: IrTypeSystemContext,
val expectToActualClassMap: Map<ClassId, IrClassSymbol>
) : ExpectActualMatchingContext<IrSymbol>, TypeSystemContext by typeContext {
private inline fun <R> CallableSymbolMarker.processIr(
onFunction: (IrFunction) -> R,
onProperty: (IrProperty) -> R,
onField: (IrField) -> R,
onValueParameter: (IrValueParameter) -> R,
onEnumEntry: (IrEnumEntry) -> R,
): R {
return when (this) {
is IrFunctionSymbol -> onFunction(owner)
is IrPropertySymbol -> onProperty(owner)
is IrFieldSymbol -> onField(owner)
is IrValueParameterSymbol -> onValueParameter(owner)
is IrEnumEntrySymbol -> onEnumEntry(owner)
else -> error("Unsupported declaration: $this")
}
}
private inline fun <R> ClassLikeSymbolMarker.processIr(
onClass: (IrClass) -> R,
onTypeAlias: (IrTypeAlias) -> R,
): R {
return when (this) {
is IrClassSymbol -> onClass(owner)
is IrTypeAliasSymbol -> onTypeAlias(owner)
else -> error("Unsupported declaration: $this")
}
}
private fun DeclarationSymbolMarker.asIr(): IrDeclaration = (this as IrSymbol).owner as IrDeclaration
private fun FunctionSymbolMarker.asIr(): IrFunction = (this as IrFunctionSymbol).owner
private fun PropertySymbolMarker.asIr(): IrProperty = (this as IrPropertySymbol).owner
private fun ValueParameterSymbolMarker.asIr(): IrValueParameter = (this as IrValueParameterSymbol).owner
private fun TypeParameterSymbolMarker.asIr(): IrTypeParameter = (this as IrTypeParameterSymbol).owner
private fun RegularClassSymbolMarker.asIr(): IrClass = (this as IrClassSymbol).owner
private fun TypeAliasSymbolMarker.asIr(): IrTypeAlias = (this as IrTypeAliasSymbol).owner
private inline fun <reified T : IrDeclaration> DeclarationSymbolMarker.safeAsIr(): T? = (this as IrSymbol).owner as? T
override val shouldCheckReturnTypesOfCallables: Boolean
get() = true
override val innerClassesCapturesOuterTypeParameters: Boolean
get() = false
override val RegularClassSymbolMarker.classId: ClassId
get() = asIr().classIdOrFail
override val TypeAliasSymbolMarker.classId: ClassId
get() = asIr().classIdOrFail
override val CallableSymbolMarker.callableId: CallableId
get() = processIr(
onFunction = { it.callableId },
onProperty = { it.callableId },
onField = { it.callableId },
onValueParameter = { shouldNotBeCalled() },
onEnumEntry = { it.callableId }
)
override val TypeParameterSymbolMarker.parameterName: Name
get() = asIr().name
override val ValueParameterSymbolMarker.parameterName: Name
get() = asIr().name
override fun TypeAliasSymbolMarker.expandToRegularClass(): RegularClassSymbolMarker? {
return asIr().expandedType.getClass()?.symbol
}
override val RegularClassSymbolMarker.classKind: ClassKind
get() = asIr().kind
override val RegularClassSymbolMarker.isCompanion: Boolean
get() = asIr().isCompanion
override val RegularClassSymbolMarker.isInner: Boolean
get() = asIr().isInner
override val RegularClassSymbolMarker.isInline: Boolean
get() = asIr().isValue
override val RegularClassSymbolMarker.isValue: Boolean
get() = asIr().isValue
override val RegularClassSymbolMarker.isFun: Boolean
get() = asIr().isFun
override val ClassLikeSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
get() {
val parameters = processIr(
onClass = { it.typeParameters },
onTypeAlias = { it.typeParameters },
)
return parameters.map { it.symbol }
}
override val ClassLikeSymbolMarker.modality: Modality
get() = processIr(
onClass = {
// For some reason kotlin annotations in IR have open modality and java annotations have final modality
// But since it's legal to actualize kotlin annotation class with java annotation class
// and effectively all annotation classes have the same modality, it's ok to always return one
// modality for all annotation classes (doesn't matter final or open)
if (it.isAnnotationClass) Modality.OPEN else it.modality
},
onTypeAlias = { Modality.FINAL }
)
override val ClassLikeSymbolMarker.visibility: Visibility
get() = safeAsIr<IrDeclarationWithVisibility>()!!.visibility.delegate
override val CallableSymbolMarker.modality: Modality?
get() = when (this) {
is IrConstructorSymbol -> Modality.FINAL
is IrSymbol -> (owner as? IrOverridableMember)?.modality
else -> shouldNotBeCalled()
}
override val CallableSymbolMarker.visibility: Visibility
get() = when (this) {
is IrEnumEntrySymbol -> Visibilities.Public
is IrSymbol -> (owner as IrDeclarationWithVisibility).visibility.delegate
else -> shouldNotBeCalled()
}
override val RegularClassSymbolMarker.superTypes: List<KotlinTypeMarker>
get() = asIr().superTypes
override val CallableSymbolMarker.isExpect: Boolean
get() = processIr(
onFunction = { it.isExpect },
onProperty = { it.isExpect },
onField = { false },
onValueParameter = { false },
onEnumEntry = { false }
)
override val CallableSymbolMarker.isInline: Boolean
get() = processIr(
onFunction = { it.isInline },
onProperty = { false }, // property can not be inline in IR. Its getter is inline instead
onField = { false },
onValueParameter = { false },
onEnumEntry = { false }
)
override val CallableSymbolMarker.isSuspend: Boolean
get() = processIr(
onFunction = { it.isSuspend },
onProperty = { false }, // property can not be suspend in IR. Its getter is suspend instead
onField = { false },
onValueParameter = { false },
onEnumEntry = { false }
)
override val CallableSymbolMarker.isExternal: Boolean
get() = safeAsIr<IrPossiblyExternalDeclaration>()?.isExternal ?: false
override val CallableSymbolMarker.isInfix: Boolean
get() = safeAsIr<IrSimpleFunction>()?.isInfix ?: false
override val CallableSymbolMarker.isOperator: Boolean
get() = safeAsIr<IrSimpleFunction>()?.isOperator ?: false
override val CallableSymbolMarker.isTailrec: Boolean
get() = safeAsIr<IrSimpleFunction>()?.isTailrec ?: false
override val PropertySymbolMarker.isVar: Boolean
get() = asIr().isVar
override val PropertySymbolMarker.isLateinit: Boolean
get() = asIr().isLateinit
override val PropertySymbolMarker.isConst: Boolean
get() = asIr().isConst
override val PropertySymbolMarker.setter: FunctionSymbolMarker?
get() = asIr().setter?.symbol
@OptIn(UnsafeCastFunction::class)
override fun createExpectActualTypeParameterSubstitutor(
expectTypeParameters: List<TypeParameterSymbolMarker>,
actualTypeParameters: List<TypeParameterSymbolMarker>,
parentSubstitutor: TypeSubstitutorMarker?,
): TypeSubstitutorMarker {
val expectParameters = expectTypeParameters.castAll<IrTypeParameterSymbol>()
val actualParameters = actualTypeParameters.castAll<IrTypeParameterSymbol>()
val actualTypes = actualParameters.map { it.owner.defaultType }
val substitutor = IrTypeSubstitutor(expectParameters, actualTypes, typeContext.irBuiltIns, allowEmptySubstitution = true)
return when (parentSubstitutor) {
null -> substitutor
is AbstractIrTypeSubstitutor -> IrChainedSubstitutor(parentSubstitutor, substitutor)
else -> shouldNotBeCalled()
}
}
/*
* [isActualDeclaration] flag is needed to correctly determine scope for specific class
* In IR there are no scopes, all declarations are stored inside IrClass itself, so this flag
* has no sense in IR context
*/
override fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List<DeclarationSymbolMarker> {
return asIr().declarations.filterNot { it is IrAnonymousInitializer }.map { it.symbol }
}
override fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List<DeclarationSymbolMarker> {
return asIr().declarations.filter { it.getNameWithAssert() == name }.map { it.symbol }
}
override fun RegularClassSymbolMarker.collectEnumEntryNames(): List<Name> {
return asIr().declarations.filterIsInstance<IrEnumEntry>().map { it.name }
}
override val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
get() = (asIr().parent as? IrClass)?.defaultType
override val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
get() = safeAsIr<IrFunction>()?.extensionReceiverParameter?.type
override val CallableSymbolMarker.returnType: KotlinTypeMarker
get() = processIr(
onFunction = { it.returnType },
onProperty = { it.getter?.returnType ?: it.backingField?.type ?: error("No type for property: $it") },
onField = { it.type },
onValueParameter = { it.type },
onEnumEntry = { it.parentAsClass.defaultType }
)
override val CallableSymbolMarker.typeParameters: List<TypeParameterSymbolMarker>
get() = processIr(
onFunction = { it.typeParameters.map { parameter -> parameter.symbol } },
onProperty = { it.getter?.symbol?.typeParameters.orEmpty() },
onField = { emptyList() },
onValueParameter = { emptyList() },
onEnumEntry = { emptyList() }
)
override val FunctionSymbolMarker.valueParameters: List<ValueParameterSymbolMarker>
get() = asIr().valueParameters.map { it.symbol }
override val ValueParameterSymbolMarker.isVararg: Boolean
get() = asIr().isVararg
override val ValueParameterSymbolMarker.isNoinline: Boolean
get() = asIr().isNoinline
override val ValueParameterSymbolMarker.isCrossinline: Boolean
get() = asIr().isCrossinline
override val ValueParameterSymbolMarker.hasDefaultValue: Boolean
get() = asIr().hasDefaultValue()
override fun CallableSymbolMarker.isAnnotationConstructor(): Boolean {
val irConstructor = safeAsIr<IrConstructor>() ?: return false
return irConstructor.constructedClass.isAnnotationClass
}
override val TypeParameterSymbolMarker.bounds: List<KotlinTypeMarker>
get() = asIr().superTypes
override val TypeParameterSymbolMarker.variance: Variance
get() = asIr().variance
override val TypeParameterSymbolMarker.isReified: Boolean
get() = asIr().isReified
override fun areCompatibleExpectActualTypes(expectType: KotlinTypeMarker?, actualType: KotlinTypeMarker?): Boolean {
if (expectType == null) return actualType == null
if (actualType == null) return false
/*
* Here we need to actualize both types, because of following situation:
*
* // MODULE: common
* expect fun foo(): S // (1)
* expect class S
*
* // MODULE: intermediate
* actual fun foo(): S = null!! // (2)
*
* // MODULE: platform
* actual typealias S = String
*
* When we match return types of (1) and (2) they both will have original type `S`, but from
* perspective of module `platform` it should be replaced with `String`
*/
val actualizedExpectType = actualizingSubstitutor.substitute(expectType as IrType)
val actualizedActualType = actualizingSubstitutor.substitute(actualType as IrType)
return AbstractTypeChecker.equalTypes(
typeContext.newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = false),
actualizedExpectType,
actualizedActualType
)
}
private val actualizingSubstitutor = ActualizingSubstitutor()
private inner class ActualizingSubstitutor : AbstractIrTypeSubstitutor() {
override fun substitute(type: IrType): IrType {
return substituteOrNull(type) ?: type
}
private fun substituteOrNull(type: IrType): IrType? {
if (type !is IrSimpleTypeImpl) return null
val newClassifier = (type.classifier.owner as? IrClass)?.let { expectToActualClassMap[it.classIdOrFail] }
val newArguments = ArrayList<IrTypeArgument>(type.arguments.size)
var argumentsChanged = false
for (argument in type.arguments) {
val newArgument = substituteArgumentOrNull(argument)
if (newArgument != null) {
newArguments += newArgument
argumentsChanged = true
} else {
newArguments += argument
}
}
if (newClassifier == null && !argumentsChanged) {
return null
}
return IrSimpleTypeImpl(
classifier = newClassifier ?: type.classifier,
type.nullability,
newArguments,
type.annotations,
type.abbreviation
)
}
private fun substituteArgumentOrNull(argument: IrTypeArgument): IrTypeArgument? {
return when (argument) {
is IrStarProjection -> null
is IrTypeProjection -> when (argument) {
is IrTypeProjectionImpl -> {
val newType = substituteOrNull(argument.type) ?: return null
makeTypeProjection(newType, argument.variance)
}
is IrTypeBase -> substituteOrNull(argument) as IrTypeBase?
else -> shouldNotBeCalled()
}
}
}
}
override fun RegularClassSymbolMarker.isNotSamInterface(): Boolean {
// TODO: is it correct for java classes?
return !asIr().isFun
}
override fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean {
return false
}
override val CallableSymbolMarker.hasStableParameterNames: Boolean
get() = when (asIr().origin) {
IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB,
-> false
else -> true
}
override fun onMatchedMembers(expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker) {
require(expectSymbol is IrSymbol)
require(actualSymbol is IrSymbol)
when (expectSymbol) {
is IrClassSymbol -> {
val actualClassSymbol = when (actualSymbol) {
is IrClassSymbol -> actualSymbol
is IrTypeAliasSymbol -> actualSymbol.owner.expandedType.getClass()!!.symbol
else -> actualSymbol.unexpectedSymbolKind<IrClassifierSymbol>()
}
onMatchedClasses(expectSymbol, actualClassSymbol)
}
else -> onMatchedCallables(expectSymbol, actualSymbol)
}
}
abstract fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol)
abstract fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol)
}
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrTypeAbbreviation
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeParameterMarker
@@ -46,7 +47,7 @@ import org.jetbrains.kotlin.types.model.TypeParameterMarker
* @see IdSignature
* @see SymbolTable
*/
interface IrSymbol {
interface IrSymbol : DeclarationSymbolMarker {
/**
* The declaration that this symbol refers to if it's bound.
@@ -167,7 +168,7 @@ interface IrAnonymousInitializerSymbol : IrBindableSymbol<ClassDescriptor, IrAno
*
* @see IrGetEnumValue
*/
interface IrEnumEntrySymbol : IrBindableSymbol<ClassDescriptor, IrEnumEntry>
interface IrEnumEntrySymbol : IrBindableSymbol<ClassDescriptor, IrEnumEntry>, EnumEntrySymbolMarker
/**
* A symbol whose [owner] is [IrField].
@@ -175,7 +176,7 @@ interface IrEnumEntrySymbol : IrBindableSymbol<ClassDescriptor, IrEnumEntry>
* @see IrGetField
* @see IrSetField
*/
interface IrFieldSymbol : IrBindableSymbol<PropertyDescriptor, IrField>
interface IrFieldSymbol : IrBindableSymbol<PropertyDescriptor, IrField>, FieldSymbolMarker
/**
* A symbol whose [owner] is [IrClass], [IrScript] or [IrTypeParameter].
@@ -195,7 +196,7 @@ sealed interface IrClassifierSymbol : IrSymbol, TypeConstructorMarker {
* @see IrCall.superQualifierSymbol
* @see IrFieldAccessExpression.superQualifierSymbol
*/
interface IrClassSymbol : IrClassifierSymbol, IrBindableSymbol<ClassDescriptor, IrClass>
interface IrClassSymbol : IrClassifierSymbol, IrBindableSymbol<ClassDescriptor, IrClass>, RegularClassSymbolMarker
/**
* A symbol whose [owner] is [IrScript].
@@ -205,7 +206,10 @@ interface IrScriptSymbol : IrClassifierSymbol, IrBindableSymbol<ScriptDescriptor
/**
* A symbol whose [owner] is [IrTypeParameter].
*/
interface IrTypeParameterSymbol : IrClassifierSymbol, IrBindableSymbol<TypeParameterDescriptor, IrTypeParameter>, TypeParameterMarker
interface IrTypeParameterSymbol : IrClassifierSymbol,
IrBindableSymbol<TypeParameterDescriptor, IrTypeParameter>,
TypeParameterMarker,
TypeParameterSymbolMarker
/**
* A symbol whose [owner] is [IrValueParameter] or [IrVariable].
@@ -223,7 +227,7 @@ sealed interface IrValueSymbol : IrSymbol {
/**
* A symbol whose [owner] is [IrValueParameter].
*/
interface IrValueParameterSymbol : IrValueSymbol, IrBindableSymbol<ParameterDescriptor, IrValueParameter>
interface IrValueParameterSymbol : IrValueSymbol, IrBindableSymbol<ParameterDescriptor, IrValueParameter>, ValueParameterSymbolMarker
/**
* A symbol whose [owner] is [IrVariable].
@@ -247,7 +251,7 @@ sealed interface IrReturnTargetSymbol : IrSymbol {
*
* @see IrFunctionReference
*/
sealed interface IrFunctionSymbol : IrReturnTargetSymbol {
sealed interface IrFunctionSymbol : IrReturnTargetSymbol, FunctionSymbolMarker {
override val owner: IrFunction
}
@@ -256,14 +260,14 @@ sealed interface IrFunctionSymbol : IrReturnTargetSymbol {
*
* @see IrConstructorCall
*/
interface IrConstructorSymbol : IrFunctionSymbol, IrBindableSymbol<ClassConstructorDescriptor, IrConstructor>
interface IrConstructorSymbol : IrFunctionSymbol, IrBindableSymbol<ClassConstructorDescriptor, IrConstructor>, ConstructorSymbolMarker
/**
* A symbol whose [owner] is [IrSimpleFunction].
*
* @see IrCall
*/
interface IrSimpleFunctionSymbol : IrFunctionSymbol, IrBindableSymbol<FunctionDescriptor, IrSimpleFunction>
interface IrSimpleFunctionSymbol : IrFunctionSymbol, IrBindableSymbol<FunctionDescriptor, IrSimpleFunction>, SimpleFunctionSymbolMarker
/**
* A symbol whose [owner] is [IrReturnableBlock].
@@ -273,7 +277,7 @@ interface IrReturnableBlockSymbol : IrReturnTargetSymbol, IrBindableSymbol<Funct
/**
* A symbol whose [owner] is [IrProperty].
*/
interface IrPropertySymbol : IrBindableSymbol<PropertyDescriptor, IrProperty>
interface IrPropertySymbol : IrBindableSymbol<PropertyDescriptor, IrProperty>, PropertySymbolMarker
/**
* A symbol whose [owner] is [IrLocalDelegatedProperty].
@@ -285,4 +289,4 @@ interface IrLocalDelegatedPropertySymbol : IrBindableSymbol<VariableDescriptorWi
*
* @see IrTypeAbbreviation
*/
interface IrTypeAliasSymbol : IrBindableSymbol<TypeAliasDescriptor, IrTypeAlias>
interface IrTypeAliasSymbol : IrBindableSymbol<TypeAliasDescriptor, IrTypeAlias>, TypeAliasSymbolMarker
@@ -13,8 +13,11 @@ import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.utils.memoryOptimizedMap
abstract class AbstractIrTypeSubstitutor : TypeSubstitutorMarker {
abstract fun substitute(type: IrType): IrType
}
abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : TypeSubstitutorMarker {
abstract class BaseIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : AbstractIrTypeSubstitutor() {
private fun IrType.typeParameterConstructor(): IrTypeParameterSymbol? {
return if (this is IrSimpleType) classifier as? IrTypeParameterSymbol
@@ -25,7 +28,7 @@ abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : T
abstract fun isEmptySubstitution(): Boolean
fun substitute(type: IrType): IrType {
final override fun substitute(type: IrType): IrType {
if (isEmptySubstitution()) return type
return substituteType(type)
}
@@ -63,8 +66,9 @@ abstract class AbstractIrTypeSubstitutor(private val irBuiltIns: IrBuiltIns) : T
class IrTypeSubstitutor(
typeParameters: List<IrTypeParameterSymbol>,
typeArguments: List<IrTypeArgument>,
irBuiltIns: IrBuiltIns
) : AbstractIrTypeSubstitutor(irBuiltIns) {
irBuiltIns: IrBuiltIns,
private val allowEmptySubstitution: Boolean = false
) : BaseIrTypeSubstitutor(irBuiltIns) {
init {
assert(typeParameters.size == typeArguments.size) {
@@ -80,7 +84,8 @@ class IrTypeSubstitutor(
override fun getSubstitutionArgument(typeParameter: IrTypeParameterSymbol): IrTypeArgument =
substitution[typeParameter]
?: throw AssertionError("Unsubstituted type parameter: ${typeParameter.owner.render()}")
?: typeParameter.takeIf { allowEmptySubstitution }?.owner?.defaultType
?: error("Unsubstituted type parameter: ${typeParameter.owner.render()}")
override fun isEmptySubstitution(): Boolean = substitution.isEmpty()
}
@@ -90,7 +95,7 @@ class IrCapturedTypeSubstitutor(
typeArguments: List<IrTypeArgument>,
capturedTypes: List<IrCapturedType?>,
irBuiltIns: IrBuiltIns
) : AbstractIrTypeSubstitutor(irBuiltIns) {
) : BaseIrTypeSubstitutor(irBuiltIns) {
init {
assert(typeArguments.size == typeParameters.size)
@@ -108,3 +113,10 @@ class IrCapturedTypeSubstitutor(
override fun isEmptySubstitution(): Boolean = oldSubstitution.isEmpty()
}
class IrChainedSubstitutor(val first: AbstractIrTypeSubstitutor, val second: AbstractIrTypeSubstitutor) : AbstractIrTypeSubstitutor() {
override fun substitute(type: IrType): IrType {
val firstResult = first.substitute(type)
return second.substitute(firstResult)
}
}
@@ -169,7 +169,7 @@ val IrClassifierSymbol.defaultType: IrType
is IrScriptSymbol -> unexpectedSymbolKind<IrClassifierSymbol>()
}
val IrTypeParameter.defaultType: IrType
val IrTypeParameter.defaultType: IrSimpleType
get() = IrSimpleTypeImpl(
symbol,
SimpleTypeNullability.NOT_SPECIFIED,
@@ -15,10 +15,7 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrClassPublicSymbolImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.utils.filterIsInstanceAnd
import java.io.File
@@ -57,12 +54,49 @@ val IrDeclarationParent.kotlinFqName: FqName
}
val IrClass.classId: ClassId?
get() = classIdImpl
val IrTypeAlias.classId: ClassId?
get() = classIdImpl
private val IrDeclarationWithName.classIdImpl: ClassId?
get() = when (val parent = this.parent) {
is IrClass -> parent.classId?.createNestedClassId(this.name)
is IrPackageFragment -> ClassId.topLevel(parent.packageFqName.child(this.name))
else -> null
}
val IrClass.classIdOrFail: ClassId
get() = classIdOrFailImpl
val IrTypeAlias.classIdOrFail: ClassId
get() = classIdOrFailImpl
private val IrDeclarationWithName.classIdOrFailImpl: ClassId
get() = classIdImpl ?: error("No classId for $this")
val IrFunction.callableId: CallableId
get() = callableIdImpl
val IrProperty.callableId: CallableId
get() = callableIdImpl
val IrField.callableId: CallableId
get() = callableIdImpl
val IrEnumEntry.callableId: CallableId
get() = callableIdImpl
private val IrDeclarationWithName.callableIdImpl: CallableId
get() {
if (this.symbol is IrClassifierSymbol) error("Classifiers can not have callableId. Got $this")
return when (val parent = this.parent) {
is IrClass -> parent.classId?.let { CallableId(it, name) }
is IrPackageFragment -> CallableId(parent.packageFqName, name)
else -> null
} ?: error("$this has no callableId")
}
@Suppress("unused")
@Deprecated(
"This function is deprecated because it has confusing name and behavior. " +
@@ -802,7 +802,7 @@ open class DeepCopyIrTreeWithSymbols(
expression.value.transform()
).copyAttributes(expression)
private fun SymbolRemapper.getReferencedReturnTarget(returnTarget: IrReturnTargetSymbol) =
private fun SymbolRemapper.getReferencedReturnTarget(returnTarget: IrReturnTargetSymbol): IrReturnTargetSymbol =
when (returnTarget) {
is IrFunctionSymbol -> getReferencedFunction(returnTarget)
is IrReturnableBlockSymbol -> getReferencedReturnableBlock(returnTarget)
@@ -29,7 +29,7 @@ object AbstractExpectActualCompatibilityChecker {
context: ExpectActualMatchingContext<T>,
): ExpectActualCompatibility<T> {
val result = with(context) {
areCompatibleClassifiers(expectClassSymbol, actualClassLikeSymbol)
areCompatibleClassifiers(expectClassSymbol, actualClassLikeSymbol, parentSubstitutor = null)
}
@Suppress("UNCHECKED_CAST")
return result as ExpectActualCompatibility<T>
@@ -50,11 +50,29 @@ object AbstractExpectActualCompatibilityChecker {
return result as ExpectActualCompatibility<T>
}
fun <T : DeclarationSymbolMarker> matchSingleExpectTopLevelDeclarationAgainstPotentialActuals(
expectDeclaration: DeclarationSymbolMarker,
actualDeclarations: List<DeclarationSymbolMarker>,
context: ExpectActualMatchingContext<T>,
) {
with(context) {
matchSingleExpectAgainstPotentialActuals(
expectDeclaration,
actualDeclarations,
substitutor = null,
expectClassSymbol = null,
actualClassSymbol = null,
unfulfilled = null
)
}
}
context(ExpectActualMatchingContext<*>)
@Suppress("warnings")
private fun areCompatibleClassifiers(
expectClassSymbol: RegularClassSymbolMarker,
actualClassLikeSymbol: ClassLikeSymbolMarker,
parentSubstitutor: TypeSubstitutorMarker?
): ExpectActualCompatibility<*> {
// Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name
require(expectClassSymbol.name == actualClassLikeSymbol.name) {
@@ -95,7 +113,7 @@ object AbstractExpectActualCompatibilityChecker {
val substitutor = createExpectActualTypeParameterSubstitutor(
expectTypeParameterSymbols,
actualTypeParameterSymbols,
parentSubstitutor = null
parentSubstitutor
)
areCompatibleTypeParameters(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor).let {
@@ -133,7 +151,7 @@ object AbstractExpectActualCompatibilityChecker {
actualClassSymbol: RegularClassSymbolMarker,
substitutor: TypeSubstitutorMarker,
): ExpectActualCompatibility<*> {
val unfulfilled = arrayListOf<Pair<Any?, Map<Incompatible<Any?>, MutableCollection<Any?>>>>()
val unfulfilled = arrayListOf<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>()
val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name }
@@ -145,31 +163,14 @@ object AbstractExpectActualCompatibilityChecker {
expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker
}.orEmpty()
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is CallableSymbolMarker -> areCompatibleCallables(
expectMember,
actualMember as CallableSymbolMarker,
substitutor,
expectClassSymbol,
actualClassSymbol
)
is RegularClassSymbolMarker -> areCompatibleClassifiers(expectMember, actualMember as RegularClassSymbolMarker)
else -> error("Unsupported declaration: $expectMember ($actualMembers)")
}
}
if (mapping.values.any { it == ExpectActualCompatibility.Compatible }) continue
val incompatibilityMap = mutableMapOf<Incompatible<Any?>, MutableCollection<Any?>>()
for ((declaration, compatibility) in mapping) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> continue@outer
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(declaration)
}
}
unfulfilled.add(expectMember to incompatibilityMap)
matchSingleExpectAgainstPotentialActuals(
expectMember,
actualMembers,
substitutor,
expectClassSymbol,
actualClassSymbol,
unfulfilled
)
}
if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) {
@@ -186,6 +187,53 @@ object AbstractExpectActualCompatibilityChecker {
return Incompatible.ClassScopes(unfulfilled)
}
context(ExpectActualMatchingContext<*>)
private fun matchSingleExpectAgainstPotentialActuals(
expectMember: DeclarationSymbolMarker,
actualMembers: List<DeclarationSymbolMarker>,
substitutor: TypeSubstitutorMarker?,
expectClassSymbol: RegularClassSymbolMarker?,
actualClassSymbol: RegularClassSymbolMarker?,
unfulfilled: MutableList<Pair<DeclarationSymbolMarker, Map<Incompatible<*>, List<DeclarationSymbolMarker?>>>>?
) {
val mapping = actualMembers.keysToMap { actualMember ->
when (expectMember) {
is CallableSymbolMarker -> areCompatibleCallables(
expectMember,
actualMember as CallableSymbolMarker,
substitutor,
expectClassSymbol,
actualClassSymbol
)
is RegularClassSymbolMarker -> {
val parentSubstitutor = substitutor?.takeIf { !innerClassesCapturesOuterTypeParameters }
areCompatibleClassifiers(
expectMember,
actualMember as ClassLikeSymbolMarker,
parentSubstitutor
)
}
else -> error("Unsupported declaration: $expectMember ($actualMembers)")
}
}
val incompatibilityMap = mutableMapOf<Incompatible<*>, MutableList<DeclarationSymbolMarker>>()
for ((actualMember, compatibility) in mapping) {
when (compatibility) {
ExpectActualCompatibility.Compatible -> {
onMatchedMembers(expectMember, actualMember)
return
}
is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(actualMember)
}
}
unfulfilled?.add(expectMember to incompatibilityMap)
onMismatchedMembersFromClassScope(expectMember, incompatibilityMap)
}
context(ExpectActualMatchingContext<*>)
private fun areCompatibleCallables(
expectDeclaration: CallableSymbolMarker,
@@ -307,6 +355,10 @@ object AbstractExpectActualCompatibilityChecker {
actualDeclaration
).let { if (it != ExpectActualCompatibility.Compatible) return it }
expectDeclaration is EnumEntrySymbolMarker && actualDeclaration is EnumEntrySymbolMarker -> {
// do nothing, entries are matched only by name
}
else -> error("Unsupported declarations: $expectDeclaration, $actualDeclaration")
}
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.mpp.*
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
@@ -20,6 +21,21 @@ import org.jetbrains.kotlin.types.model.TypeSystemContext
interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemContext {
val shouldCheckReturnTypesOfCallables: Boolean
/*
* This flag indicates how are type parameters of inner classes stored in the specific implementation of RegularClassSymbolMarker
*
* class Outer<T> {
* inner class Inner<U>
* }
*
* If flag is set to `true` then `typeParameters` for class `Outer.Inner` contains both parameters: [U, R]
* Otherwise it contains only parameters of itself: [U]
*
* This flag is needed for proper calculation of substitutions for components of inner classes
*/
val innerClassesCapturesOuterTypeParameters: Boolean
get() = true
val RegularClassSymbolMarker.classId: ClassId
val TypeAliasSymbolMarker.classId: ClassId
val CallableSymbolMarker.callableId: CallableId
@@ -100,10 +116,17 @@ interface ExpectActualMatchingContext<T : DeclarationSymbolMarker> : TypeSystemC
/*
* Determines should some declaration from expect class scope be checked
* - FE 1.0: skip fake overrides
* - FIR: skip nothing
* - FIR: skip fake overrides
* - IR: skip nothing
*/
fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean
val CallableSymbolMarker.hasStableParameterNames: Boolean
fun onMatchedMembers(expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker) {}
fun onMismatchedMembersFromClassScope(
expectSymbol: DeclarationSymbolMarker,
actualSymbolsByIncompatibility: Map<ExpectActualCompatibility.Incompatible<*>, List<DeclarationSymbolMarker>>
) {}
}
@@ -1,5 +1,6 @@
// LANGUAGE: +MultiPlatformProjects
// WITH_STDLIB
// FULL_JDK
// TARGET_BACKEND: JVM_IR
// IGNORE_BACKEND_K1: JVM_IR
// Ignore reason: expect/actual are not supported in K1 box tests
@@ -0,0 +1,49 @@
// !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT
// MODULE: m1-common
// FILE: common.kt
expect class A {
fun foo(p1: String = "common", p2: String = "common", p3: String)
}
expect class B {
fun foo(s: String)
}
interface I {
fun methodWithDefaultArg(s: String = "common")
}
expect class WithDefaultArgFromSuper : I {
override fun methodWithDefaultArg(s: String)
}
<!INCOMPATIBLE_MATCHING{JVM}!>expect open class WithIncompatibility {
fun foo(p: String = "common")
}<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
class AImpl {
fun foo(p1: String = "impl", p2: String = "impl", p3: String) {}
}
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias A = AImpl<!>
class BImpl {
fun foo(s: String = "impl") {}
}
actual typealias B = BImpl
class WithDefaultArgFromSuperImpl : I {
override fun methodWithDefaultArg(s: String) {}
}
actual typealias WithDefaultArgFromSuper = WithDefaultArgFromSuperImpl
class WithIncompatibilityImpl {
fun foo(p: String) {}
}
<!DEFAULT_ARGUMENTS_IN_EXPECT_WITH_ACTUAL_TYPEALIAS!>actual typealias WithIncompatibility = WithIncompatibilityImpl<!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT
// MODULE: m1-common
// FILE: common.kt
@@ -0,0 +1,34 @@
// MODULE: m1-common
// FILE: common.kt
<!INCOMPATIBLE_MATCHING{JVM}, INCOMPATIBLE_MATCHING{JVM}!>expect enum class Foo {
ENTRY
}<!>
expect enum class _TimeUnit
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
actual typealias Foo = FooImpl
actual typealias _TimeUnit = java.util.concurrent.TimeUnit
// FILE: FooImpl.java
public enum FooImpl {
ENTRY("OK") {
@Override
public String getResult() {
return value;
}
};
protected final String value;
public FooImpl(String value) {
this.value = value;
}
public abstract String getResult();
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// MODULE: m1-common
// FILE: common.kt
@@ -0,0 +1,17 @@
// MODULE: m1-common
// FILE: common.kt
<!INCOMPATIBLE_MATCHING{JVM}, INCOMPATIBLE_MATCHING{JVM}!>expect enum class Foo {
ENTRY1,
ENTRY2,
ENTRY3;
}<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
actual enum class Foo(val x: String) {
ENTRY1("1"),
ENTRY2("2"),
ENTRY3("3");
}
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// MODULE: m1-common
// FILE: common.kt
@@ -1,15 +1,15 @@
// MODULE: m1-common
// FILE: common.kt
// TODO: .fir.kt version is just a stub.
expect interface My {
<!INCOMPATIBLE_MATCHING{JVM}!>expect interface My {
open fun openFunPositive()
open fun openFunNegative()
<!INCOMPATIBLE_MATCHING{JVM}!>open fun openFunNegative()<!>
abstract fun abstractFun()
open val openValPositive: Int
open val openValNegative: Int
<!INCOMPATIBLE_MATCHING{JVM}!>open val openValNegative: Int<!>
abstract val abstractVal: Int
}
}<!>
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@@ -4,7 +4,7 @@
interface A
interface B
expect fun <T> List<T>.foo() where T : A, T : B
<!INCOMPATIBLE_MATCHING{JVM}!>expect fun <T> List<T>.foo() where T : A, T : B<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
@@ -7,7 +7,7 @@ expect annotation class Foo3
expect annotation class Foo4
expect annotation class Foo5()
expect annotation class Foo6()
expect annotation class Foo7()
<!INCOMPATIBLE_MATCHING{JVM}!>expect annotation class Foo7<!INCOMPATIBLE_MATCHING{JVM}!>()<!><!>
@<!UNRESOLVED_REFERENCE!>Foo1<!>
fun foo() {}
@@ -5,9 +5,9 @@ expect class Foo1
expect class Foo2
expect class Foo3
expect class Bar1()
expect class Bar2()
expect class Bar3()
<!INCOMPATIBLE_MATCHING{JVM}!>expect class Bar1<!INCOMPATIBLE_MATCHING{JVM}!>()<!><!>
<!INCOMPATIBLE_MATCHING{JVM}!>expect class Bar2<!INCOMPATIBLE_MATCHING{JVM}!>()<!><!>
<!INCOMPATIBLE_MATCHING{JVM}!>expect class Bar3<!INCOMPATIBLE_MATCHING{JVM}!>()<!><!>
expect class Bar4()
expect class Bar5()
expect class Bar6()
@@ -3,12 +3,12 @@
// FILE: common.kt
expect fun foo1(x: Int)
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo2(x: Int)<!>
<!INCOMPATIBLE_MATCHING{JVM}, INCOMPATIBLE_MATCHING{JVM}!>expect fun foo2(x: Int)<!>
expect class NoArgConstructor()
expect fun foo3(): Int
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun foo4(): Int<!>
<!INCOMPATIBLE_MATCHING{JVM}!>expect fun foo3(): Int<!>
<!INCOMPATIBLE_MATCHING{JVM}!>expect fun foo4(): Int<!>
// MODULE: m2-jvm()()(m1-common)
@@ -5,9 +5,9 @@ expect fun interface F1 {
fun run()
}
expect fun interface F2 {
<!INCOMPATIBLE_MATCHING{JVM}!>expect fun interface F2 {
fun run()
}
}<!>
expect fun interface F3 {
fun run()
@@ -21,9 +21,9 @@ expect fun interface F5 {
fun run()
}
expect fun interface F6 {
<!INCOMPATIBLE_MATCHING{JVM}!>expect fun interface F6 {
fun run()
}
}<!>
// MODULE: m2-jvm()()(m1-common)
@@ -0,0 +1,37 @@
// !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT
// MODULE: m1-common
// FILE: common.kt
expect class C1
<!INCOMPATIBLE_MATCHING{JVM}!>expect interface C2<A><!>
<!INCOMPATIBLE_MATCHING{JVM}!>expect interface C3<B><!>
expect interface C4<D, E>
expect interface C5<F, G>
expect interface C6<H>
expect interface C7<I>
expect interface C8<J>
expect interface C9<K>
expect interface C10<L>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
class A<T : A<T>>
class B<T>
actual typealias C1 = String
<!ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE!>actual typealias C2<A> = List<String><!>
<!ACTUAL_TYPE_ALIAS_TO_CLASS_WITH_DECLARATION_SITE_VARIANCE!>actual typealias C3<B> = List<B><!>
actual typealias C4<D, E> = MutableMap<D, E>
<!ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION!>actual typealias C5<F, G> = MutableMap<G, F><!>
<!ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION!>actual typealias C51 = MutableMap<String, String><!>
<!ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION!>actual typealias C52<F> = MutableMap<F, String><!>
<!ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION!>actual typealias C53<T> = A<A<T>><!>
<!ACTUAL_TYPE_ALIAS_WITH_COMPLEX_SUBSTITUTION!>actual typealias C54<T> = B<List<String>><!>
actual typealias C6<H> = MutableList<H>
<!ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE!>actual typealias C7<I> = MutableList<out I><!>
<!ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE!>actual typealias C8<J> = MutableList<*><!>
<!ACTUAL_TYPE_ALIAS_WITH_USE_SITE_VARIANCE!>actual typealias C9<K> = MutableList<in K><!>
typealias Tmp<K> = MutableList<K>
<!ACTUAL_TYPE_ALIAS_NOT_TO_CLASS!>actual typealias C10<L> = Tmp<L><!>
@@ -1,4 +1,3 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT
// MODULE: m1-common
// FILE: common.kt
@@ -2,20 +2,20 @@
// MODULE: m1-common
// FILE: common.kt
expect open class Container {
<!INCOMPATIBLE_MATCHING{JVM}!>expect open class Container {
fun publicFun()
internal fun internalFun1()
internal fun internalFun2()
internal fun internalFun3()
<!INCOMPATIBLE_MATCHING{JVM}!>internal fun internalFun3()<!>
protected fun protectedFun1()
protected fun protectedFun2()
protected fun protectedFun3()
<!INCOMPATIBLE_MATCHING{JVM}!>protected fun protectedFun3()<!>
open internal fun openInternalFun()
open fun openPublicFun()
}
<!INCOMPATIBLE_MATCHING{JVM}!>open internal fun openInternalFun()<!>
<!INCOMPATIBLE_MATCHING{JVM}!>open fun openPublicFun()<!>
}<!>
// MODULE: m2-jvm()()(m1-common)
@@ -14,9 +14,9 @@ expect class C {
<!WRONG_MODIFIER_TARGET!>expect<!> inner class I
}
expect class D {
<!INCOMPATIBLE_MATCHING{JVM}!>expect class D {
<!NO_ACTUAL_FOR_EXPECT{JVM}!>class N<!>
}
}<!>
expect class E {
class N
@@ -1,10 +1,10 @@
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
<!INCOMPATIBLE_MATCHING{JVM}!>expect class Foo {
fun bar(): String
fun bas(f: Int)
}
<!INCOMPATIBLE_MATCHING{JVM}!>fun bas(f: Int)<!>
}<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
@@ -7,7 +7,7 @@ interface J
expect class Foo : I, C, J
<!SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR!>expect class Bar : C()<!>
<!INCOMPATIBLE_MATCHING{JVM}, SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR, SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR!>expect class Bar : C()<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
@@ -2,7 +2,7 @@
// FILE: common.kt
class Foo {
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY, NO_ACTUAL_FOR_EXPECT{JVM}!><!WRONG_MODIFIER_TARGET!>expect<!> fun bar(): String<!>
<!NON_ABSTRACT_FUNCTION_WITH_NO_BODY!><!WRONG_MODIFIER_TARGET!>expect<!> fun bar(): String<!>
}
// MODULE: m1-jvm()()(m1-common)
@@ -26,7 +26,7 @@ interface KotlinXStringDemoInterface {
val value: String
}
expect fun StringDemoInterface.plusK(): String
<!INCOMPATIBLE_MATCHING{JS}!>expect fun StringDemoInterface.plusK(): String<!>
// MODULE: js()()(common, intermediate)
// TARGET_PLATFORM: JS
@@ -34,7 +34,7 @@ expect fun StringDemoInterface.plusK(): String
// FILE: StringDemoInterface.kt
actual typealias StringDemoInterface = KotlinXStringDemoInterface
<!ACTUAL_WITHOUT_EXPECT("actual fun StringDemoInterface.plusK(): <ERROR TYPE REF: Unresolved name: value>; The following declaration is incompatible: expect fun StringDemoInterface.plusK(): String")!>actual fun StringDemoInterface.plusK() = <!RESOLUTION_TO_CLASSIFIER!>StringValue<!>(value).plus("K").<!UNRESOLVED_REFERENCE!>value<!><!>
<!ACTUAL_WITHOUT_EXPECT("actual fun StringDemoInterface.plusK(): <ERROR TYPE REF: Unresolved name: value>; The following declaration is incompatible: expect fun StringDemoInterface.plusK(): String")!>actual fun StringDemoIn<!INCOMPATIBLE_MATCHING!>terface.plusK() = <!RESOLUTION_TO_CLASSIFIER!>StringValue<!>(value).plus("K")<!>.<!UNRESOLVED_REFERENCE!>value<!><!>
// FILE: main.kt
class StringDemo(override val value: String) : StringDemoInterface
@@ -3,10 +3,10 @@
// TARGET_PLATFORM: Common
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect class CommonClass {
<!NO_ACTUAL_FOR_EXPECT{JVM}!>fun memberFun()<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!>val memberProp: Int<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!>class Nested<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!>inner class Inner<!>
fun memberFun()
val memberProp: Int
class Nested
inner class Inner
}<!>
<!ACTUAL_WITHOUT_EXPECT!>actual class CommonClass {
<!ACTUAL_WITHOUT_EXPECT!>actual fun memberFun() {}<!>
@@ -13,8 +13,8 @@ val callableKind: Int = 1
expect fun <T> typeParameterCount()
fun typeParameterCount() {}
<!NO_ACTUAL_FOR_EXPECT{JVM}, NO_ACTUAL_FOR_EXPECT{JVM}, NO_ACTUAL_FOR_EXPECT{JVM}, NO_ACTUAL_FOR_EXPECT{JVM}!>expect enum class EnumEntries {
<!NO_ACTUAL_FOR_EXPECT{JVM}!>ONE,<!> <!NO_ACTUAL_FOR_EXPECT{JVM}!>TWO;<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect enum class EnumEntries {
ONE, TWO;
}<!>
<!ACTUAL_WITHOUT_EXPECT!>actual enum class EnumEntries {
ONE;
@@ -1,31 +0,0 @@
// MODULE: m1-common
// FILE: common.kt
expect class Foo {
constructor(p: Any)
fun f1(s: String): Int
<!NO_ACTUAL_FOR_EXPECT{JVM}!>fun f2(s: List<String>?): MutableMap<Boolean?, Foo><!>
fun <T : Set<Number>> f3(t: T): T?
}
// MODULE: m2-jvm()()(m1-common)
// FILE: FooImpl.java
import java.util.*;
public class FooImpl {
public FooImpl(Object p) {}
public final int f1(String s) { return 0; }
public final Map<Boolean, FooImpl> f2(List<String> s) { return null; }
public final <T extends Set<Number>> T f3(T t) { return null; }
}
// FILE: jvm.kt
actual typealias Foo = FooImpl
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// MODULE: m1-common
// FILE: common.kt
@@ -1,8 +1,8 @@
// MODULE: m1-common
// FILE: common.kt
expect class SomeClass<T> {
<!INCOMPATIBLE_MATCHING{JVM}!>expect class SomeClass<T> {
fun foo()
}
}<!>
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@@ -1,7 +1,7 @@
// MODULE: m1-common
// FILE: common.kt
inline expect fun inlineFun()
<!INCOMPATIBLE_MATCHING{JVM}!>inline expect fun inlineFun()<!>
expect fun nonInlineFun()
// MODULE: m2-jvm()()(m1-common)
@@ -3,15 +3,15 @@
// FILE: common.kt
expect fun f1(s: () -> String)
expect inline fun f2(s: () -> String)
<!INCOMPATIBLE_MATCHING{JVM}!>expect inline fun f2(s: () -> String)<!>
expect inline fun f3(noinline s: () -> String)
expect fun f4(s: () -> String)
expect inline fun f5(s: () -> String)
<!INCOMPATIBLE_MATCHING{JVM}!>expect inline fun f5(s: () -> String)<!>
expect inline fun f6(crossinline s: () -> String)
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun f7(x: Any)<!>
<!NO_ACTUAL_FOR_EXPECT{JVM}!>expect fun f8(vararg x: Any)<!>
<!INCOMPATIBLE_MATCHING{JVM}!>expect fun f7(x: Any)<!>
<!INCOMPATIBLE_MATCHING{JVM}!>expect fun f8(vararg x: Any)<!>
// MODULE: m2-jvm()()(m1-common)
// FILE: jvm.kt
@@ -1,20 +1,20 @@
// MODULE: m1-common
// FILE: common.kt
expect var v1: Boolean
<!INCOMPATIBLE_MATCHING{JVM}!>expect var v1: Boolean<!>
expect var v2: Boolean
internal set
expect var v3: Boolean
internal set
<!INCOMPATIBLE_MATCHING{JVM}!>expect var v3: Boolean
internal set<!>
expect open class C {
var foo: Boolean
}
<!INCOMPATIBLE_MATCHING{JVM}!>expect open class C {
<!INCOMPATIBLE_MATCHING{JVM}!>var foo: Boolean<!>
}<!>
expect open class C2 {
var foo: Boolean
}
<!INCOMPATIBLE_MATCHING{JVM}!>expect open class C2 {
<!INCOMPATIBLE_MATCHING{JVM}!>var foo: Boolean<!>
}<!>
// MODULE: m1-jvm()()(m1-common)
// FILE: jvm.kt
@@ -6,8 +6,11 @@
package org.jetbrains.kotlin.test.backend.ir
import org.jetbrains.kotlin.backend.common.actualizer.IrActualizer
import org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.platform.jvm.isJvm
import org.jetbrains.kotlin.test.model.*
import org.jetbrains.kotlin.test.services.TestServices
import org.jetbrains.kotlin.test.services.compilerConfigurationProvider
@@ -17,10 +20,16 @@ class ActualizerOnlyFacade(
) : AbstractTestFacade<IrBackendInput, IrBackendInput>() {
override fun transform(module: TestModule, inputArtifact: IrBackendInput): IrBackendInput {
if (module.useIrActualizer()) {
val builtins = inputArtifact.irModuleFragment.irBuiltins
val typeSystemContext = when (module.targetPlatform.isJvm()) {
true -> JvmIrTypeSystemContext(builtins)
false -> IrTypeSystemContextImpl(builtins)
}
IrActualizer.actualize(
inputArtifact.irModuleFragment,
inputArtifact.dependentIrModuleFragments,
inputArtifact.diagnosticReporter,
typeSystemContext,
testServices.compilerConfigurationProvider.getCompilerConfiguration(module).languageVersionSettings
)
}
@@ -35,4 +44,4 @@ class ActualizerOnlyFacade(
override val outputKind: TestArtifactKind<IrBackendInput> = BackendKinds.IrBackend
override fun shouldRunAnalysis(module: TestModule): Boolean = true
}
}
@@ -8,12 +8,14 @@ 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.JvmIrTypeSystemContext
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
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.backend.classic.JavaCompilerFacade
@@ -40,6 +42,7 @@ class JvmIrBackendFacade(
inputArtifact.irModuleFragment,
inputArtifact.dependentIrModuleFragments,
inputArtifact.state.diagnosticReporter,
JvmIrTypeSystemContext(inputArtifact.irModuleFragment.irBuiltins),
inputArtifact.state.languageVersionSettings
)
}
@@ -17,6 +17,8 @@ interface ConstructorSymbolMarker : FunctionSymbolMarker
interface SimpleFunctionSymbolMarker : FunctionSymbolMarker
interface PropertySymbolMarker : CallableSymbolMarker
interface ValueParameterSymbolMarker : CallableSymbolMarker
interface FieldSymbolMarker : CallableSymbolMarker
interface EnumEntrySymbolMarker : CallableSymbolMarker
interface ClassifierSymbolMarker : DeclarationSymbolMarker
interface TypeParameterSymbolMarker : ClassifierSymbolMarker
@@ -5,6 +5,9 @@
package org.jetbrains.kotlin.resolve.multiplatform
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
sealed class ExpectActualCompatibility<out D> {
// For IncompatibilityKind.STRONG `actual` declaration is considered as overload and error reports on expected declaration
enum class IncompatibilityKind {
@@ -95,3 +98,11 @@ sealed class ExpectActualCompatibility<out D> {
val ExpectActualCompatibility<*>.compatible: Boolean
get() = this == ExpectActualCompatibility.Compatible
@OptIn(ExperimentalContracts::class)
fun ExpectActualCompatibility<*>.isIncompatible(): Boolean {
contract {
returns(true) implies (this@isIncompatible is ExpectActualCompatibility.Incompatible<*>)
}
return !compatible
}
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.backend.js.JsFactories
import org.jetbrains.kotlin.ir.backend.js.resolverLogger
import org.jetbrains.kotlin.ir.backend.js.serializeModuleIntoKlib
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.js.test.utils.JsIrIncrementalDataProvider
import org.jetbrains.kotlin.js.test.utils.jsIrIncrementalDataProvider
@@ -71,6 +72,7 @@ class FirJsKlibBackendFacade(
inputArtifact.irModuleFragment,
inputArtifact.dependentIrModuleFragments,
diagnosticReporter,
IrTypeSystemContextImpl(inputArtifact.irModuleFragment.irBuiltins),
configuration.languageVersionSettings
)
} else {
@@ -124,4 +126,4 @@ class FirJsKlibBackendFacade(
return BinaryArtifacts.KLib(File(outputFile))
}
}
}
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.fir.signaturer.Ir2FirManglerAdapter
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.library.metadata.KlibMetadataFactories
@@ -87,6 +88,7 @@ internal fun PhaseContext.fir2Ir(
visibilityConverter = Fir2IrVisibilityConverter.Default,
diagnosticReporter = diagnosticsReporter,
kotlinBuiltIns = builtInsModule ?: DefaultBuiltIns.Instance,
actualizerTypeContextProvider = ::IrTypeSystemContextImpl,
fir2IrResultPostCompute = {
// it's important to compare manglers before actualization, since IR will be actualized, while FIR won't
irModuleFragment.acceptVoid(