From b19058caef6a8b7e5b4ee8df9e16650c25ae2428 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Fri, 10 Feb 2023 20:17:05 +0000 Subject: [PATCH] K2/Native: support cinterop forward declarations Support importing synthetic declarations from "magic" forward declaration packages: - cnames.structs - objcnames.classes - objcnames.protocols So this is a rough equivalent to the forward declarations module made of ForwardDeclarationsPackageFragmentDescriptor in K1. Unlike K1 implementation, this K2 one doesn't allow importing a declaration that wasn't actually forward-declared in a C/Objective-C header available through a cinterop klib dependency. --- .../fir/session/FirNativeSessionFactory.kt | 8 + ...NativeForwardDeclarationsSymbolProvider.kt | 148 ++++++++++++++++++ ...libResolvedModuleDescriptorsFactoryImpl.kt | 29 ++-- 3 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeForwardDeclarationsSymbolProvider.kt diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt index 702f45a0c47..24e6fd057ef 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirNativeSessionFactory.kt @@ -38,8 +38,16 @@ object FirNativeSessionFactory : FirAbstractSessionFactory() { registerExtraComponents, createKotlinScopeProvider = { FirKotlinScopeProvider { _, declaredMemberScope, _, _ -> declaredMemberScope } }, createProviders = { session, builtinsModuleData, kotlinScopeProvider -> + val forwardDeclarationsModuleData = BinaryModuleData.createDependencyModuleData( + Name.special(""), + moduleDataProvider.platform, + moduleDataProvider.analyzerServices, + ).apply { + bindSession(session) + } listOf( KlibBasedSymbolProvider(session, moduleDataProvider, kotlinScopeProvider, resolvedLibraries), + NativeForwardDeclarationsSymbolProvider(session, forwardDeclarationsModuleData, kotlinScopeProvider, resolvedLibraries), FirBuiltinSymbolProvider(session, builtinsModuleData, kotlinScopeProvider), FirExtensionSyntheticFunctionInterfaceProvider(session, builtinsModuleData, kotlinScopeProvider), FirCloneableSymbolProvider(session, builtinsModuleData, kotlinScopeProvider), diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeForwardDeclarationsSymbolProvider.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeForwardDeclarationsSymbolProvider.kt new file mode 100644 index 00000000000..f056eb030ea --- /dev/null +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/NativeForwardDeclarationsSymbolProvider.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.session + +import org.jetbrains.kotlin.descriptors.EffectiveVisibility +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fir.FirModuleData +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.caches.createCache +import org.jetbrains.kotlin.fir.caches.firCachesFactory +import org.jetbrains.kotlin.fir.caches.getValue +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirResolvePhase +import org.jetbrains.kotlin.fir.declarations.builder.buildRegularClass +import org.jetbrains.kotlin.fir.declarations.getDeprecationsProvider +import org.jetbrains.kotlin.fir.declarations.impl.FirResolvedDeclarationStatusImpl +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProvider +import org.jetbrains.kotlin.fir.resolve.providers.FirSymbolProviderInternals +import org.jetbrains.kotlin.fir.scopes.FirKotlinScopeProvider +import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.types.builder.buildResolvedTypeRef +import org.jetbrains.kotlin.fir.types.constructClassType +import org.jetbrains.kotlin.library.includedForwardDeclarations +import org.jetbrains.kotlin.library.isInterop +import org.jetbrains.kotlin.library.metadata.impl.ForwardDeclarationKind +import org.jetbrains.kotlin.library.metadata.resolver.KotlinResolvedLibrary +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +class NativeForwardDeclarationsSymbolProvider( + session: FirSession, + private val forwardDeclarationsModuleData: FirModuleData, + private val kotlinScopeProvider: FirKotlinScopeProvider, + private val resolvedLibraries: Collection, +) : FirSymbolProvider(session) { + private companion object { + private val validPackages = ForwardDeclarationKind.packageFqNameToKind.keys + } + + private val includedForwardDeclarations: Set by lazy { + buildSet { + for (resolvedLibrary in resolvedLibraries) { + val library = resolvedLibrary.library + if (!library.isInterop) continue + + for (fqName in library.includedForwardDeclarations) { + val classId = ClassId.topLevel(FqName(fqName)) + if (classId.packageFqName in validPackages) add(classId) + } + } + } + } + + private val includedForwardDeclarationsByPackage: Map> by lazy { + buildMap> { + for (classId in includedForwardDeclarations) { + getOrPut(classId.packageFqName) { mutableSetOf() } + .add(classId.shortClassName.asString()) + } + } + } + + override fun getClassLikeSymbolByClassId(classId: ClassId): FirClassLikeSymbol<*>? { + if (classId !in includedForwardDeclarations) return null + if (classId.isNestedClass) return null + + return syntheticForwardDeclarationClassCache.getValue(classId) + } + + private val syntheticForwardDeclarationClassCache = + session.firCachesFactory.createCache(::createSyntheticForwardDeclarationClass) + + private fun createSyntheticForwardDeclarationClass(classId: ClassId): FirClassLikeSymbol<*>? { + val forwardDeclarationKind = ForwardDeclarationKind.packageFqNameToKind[classId.packageFqName] ?: return null + + val symbol = FirRegularClassSymbol(classId) + + buildRegularClass { + moduleData = forwardDeclarationsModuleData + origin = FirDeclarationOrigin.Synthetic + check(!classId.isNestedClass) { "Expected top-level class when building forward declaration, got $classId" } + name = classId.shortClassName + status = FirResolvedDeclarationStatusImpl( + Visibilities.Public, + Modality.FINAL, + EffectiveVisibility.Public + ).apply { + // This will be wrong if we support exported forward declarations. + // See https://youtrack.jetbrains.com/issue/KT-51377 for more details. + isExpect = false + + isActual = false + isCompanion = false + isInner = false + isData = false + isInline = false + isExternal = false + isFun = false + } + classKind = forwardDeclarationKind.classKind + scopeProvider = kotlinScopeProvider + this.symbol = symbol + + resolvePhase = FirResolvePhase.ANALYZED_DEPENDENCIES + + superTypeRefs += buildResolvedTypeRef { + type = ConeClassLikeLookupTagImpl(forwardDeclarationKind.superClassId) + .constructClassType(emptyArray(), isNullable = false) + } + }.apply { + replaceDeprecationsProvider(getDeprecationsProvider(session)) + } + + return symbol + } + + @FirSymbolProviderInternals + override fun getTopLevelCallableSymbolsTo(destination: MutableList>, packageFqName: FqName, name: Name) { + } + + @FirSymbolProviderInternals + override fun getTopLevelFunctionSymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + } + + @FirSymbolProviderInternals + override fun getTopLevelPropertySymbolsTo(destination: MutableList, packageFqName: FqName, name: Name) { + } + + override fun getPackage(fqName: FqName): FqName? { + if (fqName in includedForwardDeclarationsByPackage) { + return fqName + } + return null + } + + override fun knownTopLevelClassifiersInPackage(packageFqName: FqName): Set { + return includedForwardDeclarationsByPackage[packageFqName].orEmpty() + } + + override fun computePackageSetWithTopLevelCallables(): Set = emptySet() + + override fun computeCallableNamesInPackage(packageFqName: FqName): Set = emptySet() +} \ No newline at end of file diff --git a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/impl/KlibResolvedModuleDescriptorsFactoryImpl.kt b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/impl/KlibResolvedModuleDescriptorsFactoryImpl.kt index 14f4110f458..b5cc8c4620b 100644 --- a/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/impl/KlibResolvedModuleDescriptorsFactoryImpl.kt +++ b/compiler/util-klib-metadata/src/org/jetbrains/kotlin/library/metadata/impl/KlibResolvedModuleDescriptorsFactoryImpl.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.metadata.* import org.jetbrains.kotlin.library.metadata.resolver.KotlinLibraryResolveResult +import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns @@ -120,22 +121,18 @@ class KlibResolvedModuleDescriptorsFactoryImpl( val module = createDescriptorOptionalBuiltsIns(FORWARD_DECLARATIONS_MODULE_NAME, storageManager, builtIns, SyntheticModulesOrigin) - fun createPackage(fqName: FqName, supertypeName: String, classKind: ClassKind) = + fun createPackage(forwardDeclarationKind: ForwardDeclarationKind) = ForwardDeclarationsPackageFragmentDescriptor( storageManager, module, - fqName, - Name.identifier(supertypeName), - classKind, + forwardDeclarationKind.packageFqName, + Name.identifier(forwardDeclarationKind.superClassName), + forwardDeclarationKind.classKind, isExpect ) val packageFragmentProvider = PackageFragmentProviderImpl( - listOf( - createPackage(ForwardDeclarationsFqNames.cNamesStructs, "COpaque", ClassKind.CLASS), - createPackage(ForwardDeclarationsFqNames.objCNamesClasses, "ObjCObjectBase", ClassKind.CLASS), - createPackage(ForwardDeclarationsFqNames.objCNamesProtocols, "ObjCObject", ClassKind.INTERFACE) - ) + ForwardDeclarationKind.values().map { createPackage(it) } ) module.initialize(packageFragmentProvider) @@ -235,3 +232,17 @@ object ForwardDeclarationsFqNames { val syntheticPackages = setOf(cNames, objCNames) } + +enum class ForwardDeclarationKind(val packageFqName: FqName, val superClassName: String, val classKind: ClassKind) { + CNAMES_STRUCTS(ForwardDeclarationsFqNames.cNamesStructs, "COpaque", ClassKind.CLASS), + OBJCNAMES_CLASSES(ForwardDeclarationsFqNames.objCNamesClasses, "ObjCObjectBase", ClassKind.CLASS), + OBJCNAMES_PROTOCOLS(ForwardDeclarationsFqNames.objCNamesProtocols, "ObjCObject", ClassKind.INTERFACE) + + ; + + val superClassId = ClassId.topLevel(ForwardDeclarationsFqNames.cInterop.child(Name.identifier(superClassName))) + + companion object { + val packageFqNameToKind: Map = ForwardDeclarationKind.values().associateBy { it.packageFqName } + } +} \ No newline at end of file