[IR] Use common expect/actual matching algorithm in IR actualizer
^KT-58578 Fixed
This commit is contained in:
committed by
Space Team
parent
62534f43c9
commit
ba41e8ec38
@@ -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
|
||||
|
||||
+19
@@ -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>"
|
||||
}
|
||||
}
|
||||
|
||||
+62
-22
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+140
-109
@@ -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 }
|
||||
}
|
||||
+2
-2
@@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+83
-161
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+401
@@ -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)
|
||||
|
||||
+80
-28
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
+24
-1
@@ -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
|
||||
|
||||
+49
@@ -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
@@ -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
|
||||
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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() {}
|
||||
|
||||
+3
-3
@@ -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
-3
@@ -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)
|
||||
|
||||
|
||||
+4
-4
@@ -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)
|
||||
|
||||
|
||||
Vendored
+37
@@ -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
@@ -1,4 +1,3 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -ACTUAL_WITHOUT_EXPECT
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
+6
-6
@@ -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)
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -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
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+4
-4
@@ -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() {}<!>
|
||||
|
||||
+2
-2
@@ -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
-1
@@ -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)
|
||||
|
||||
Vendored
+4
-4
@@ -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
|
||||
|
||||
+9
-9
@@ -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
|
||||
|
||||
+10
-1
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -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
|
||||
|
||||
+11
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user