diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt index 6087fdd8f86..625581cdb7a 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.kt @@ -328,7 +328,7 @@ object KotlinToJVMBytecodeCompiler { project, environment.createPackagePartProvider(librariesScope) ) - val moduleInfo = FirJvmModuleInfo(module.getModuleName(), listOf(librariesModuleInfo)) + val moduleInfo = FirJvmModuleInfo(module, listOf(librariesModuleInfo)) val session = FirSessionFactory.createJavaModuleBasedSession(moduleInfo, provider, scope, project) { if (extendedAnalysisMode) { registerExtendedCommonCheckers() diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt index f80acef9c82..b4ab7cfed61 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/ComponentsContainers.kt @@ -58,6 +58,7 @@ fun FirSession.registerResolveComponents() { @OptIn(SessionConfiguration::class) fun FirSession.registerJavaSpecificResolveComponents() { register(FirVisibilityChecker::class, FirJavaVisibilityChecker) + register(FirModuleVisibilityChecker::class, FirJvmModuleVisibilityChecker(this)) register(ConeCallConflictResolverFactory::class, JvmCallConflictResolverFactory) register(FirEffectiveVisibilityResolver::class, FirJvmEffectiveVisibilityResolver(this)) register(FirPlatformClassMapper::class, FirJavaClassMapper(this)) diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJvmModuleInfo.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJvmModuleInfo.kt index e342b7f50e3..83a709cce48 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJvmModuleInfo.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJvmModuleInfo.kt @@ -6,23 +6,30 @@ package org.jetbrains.kotlin.fir.session import org.jetbrains.kotlin.analyzer.ModuleInfo +import org.jetbrains.kotlin.modules.Module import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.TargetPlatform import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.resolve.PlatformDependentAnalyzerServices import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatformAnalyzerServices -class FirJvmModuleInfo(override val name: Name, val dependencies: List) : ModuleInfo { +class FirJvmModuleInfo( + override val name: Name, + val dependencies: List = emptyList(), + val friendPaths: List = emptyList(), + val outputDirectory: String? = null +) : ModuleInfo { companion object { val LIBRARIES_MODULE_NAME = Name.special("") fun createForLibraries(mainModuleName: String? = null): FirJvmModuleInfo { val name = mainModuleName?.let { Name.special("") } ?: LIBRARIES_MODULE_NAME - return FirJvmModuleInfo(name, emptyList()) + return FirJvmModuleInfo(name) } } - constructor(moduleName: String, dependencies: List) : this(Name.identifier(moduleName), dependencies) + constructor(module: Module, dependencies: List) : + this(Name.identifier(module.getModuleName()), dependencies, module.getFriendPaths(), module.getOutputDirectory()) override val platform: TargetPlatform get() = JvmPlatforms.unspecifiedJvmPlatform @@ -30,7 +37,6 @@ class FirJvmModuleInfo(override val name: Name, val dependencies: List { - return dependencies - } + override fun dependencies(): List = + dependencies } diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJvmModuleVisibilityChecker.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJvmModuleVisibilityChecker.kt new file mode 100644 index 00000000000..82392c7b648 --- /dev/null +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/session/FirJvmModuleVisibilityChecker.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2021 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 com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.openapi.vfs.VfsUtilCore +import org.jetbrains.kotlin.fir.FirModuleVisibilityChecker +import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration +import org.jetbrains.kotlin.fir.declarations.containerSource +import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource +import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement +import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass +import java.nio.file.Paths + +class FirJvmModuleVisibilityChecker(private val session: FirSession) : FirModuleVisibilityChecker { + override fun isInFriendModule(declaration: FirMemberDeclaration): Boolean { + val moduleInfo = session.moduleInfo as? FirJvmModuleInfo ?: return false + val binaryClass = when (val source = declaration.containerSource) { + is KotlinJvmBinarySourceElement -> source.binaryClass + is JvmPackagePartSource -> source.knownJvmBinaryClass + else -> null + } as? VirtualFileKotlinClass ?: return false + // For incremental compilation, the already compiled part of the module should be accessible. + return moduleInfo.friendPaths.any { binaryClass.isIn(it) } || moduleInfo.outputDirectory?.let { binaryClass.isIn(it) } == true + } + + private fun VirtualFileKotlinClass.isIn(jarOrDirectory: String): Boolean = + when (file.fileSystem.protocol) { + StandardFileSystems.FILE_PROTOCOL -> + VfsUtilCore.virtualToIoFile(file).toPath().startsWith(jarOrDirectory) + StandardFileSystems.JAR_PROTOCOL -> + VfsUtilCore.getVirtualFileForJar(file)?.let(VfsUtilCore::virtualToIoFile)?.toPath() == Paths.get(jarOrDirectory) + else -> false + } +} diff --git a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt index ac5d59a33cd..82ad9989de7 100644 --- a/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt +++ b/compiler/fir/fir-deserialization/src/org/jetbrains/kotlin/fir/deserialization/FirMemberDeserializer.kt @@ -449,6 +449,7 @@ class FirMemberDeserializer(private val c: FirDeserializationContext) { ) annotations += c.annotationDeserializer.loadConstructorAnnotations(c.containerSource, proto, local.nameResolver, local.typeTable) + containerSource = c.containerSource }.build().apply { containingClassAttr = c.dispatchReceiver!!.lookupTag versionRequirementsTable = c.versionRequirementTable diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt index 542cb938d3d..719f95c1c95 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirVisibilityChecker.kt @@ -22,6 +22,10 @@ import org.jetbrains.kotlin.fir.types.ConeClassLikeType import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +interface FirModuleVisibilityChecker : FirSessionComponent { + fun isInFriendModule(declaration: FirMemberDeclaration): Boolean +} + abstract class FirVisibilityChecker : FirSessionComponent { @NoMutableState object Default : FirVisibilityChecker() { @@ -54,14 +58,13 @@ abstract class FirVisibilityChecker : FirSessionComponent { val session = callInfo.session val provider = session.firProvider - return when (declaration.visibility) { Visibilities.Internal -> { - declaration.session == callInfo.session + declaration.session == session || session.moduleVisibilityChecker?.isInFriendModule(declaration) == true } Visibilities.Private, Visibilities.PrivateToThis -> { val ownerId = symbol.getOwnerId() - if (declaration.session == callInfo.session) { + if (declaration.session == session) { if (ownerId == null || declaration is FirConstructor && declaration.isFromSealedClass) { val candidateFile = when (symbol) { is FirClassLikeSymbol<*> -> provider.getFirClassifierContainerFileIfAny(symbol) @@ -194,6 +197,7 @@ abstract class FirVisibilityChecker : FirSessionComponent { } } +val FirSession.moduleVisibilityChecker: FirModuleVisibilityChecker? by FirSession.nullableSessionComponentAccessor() val FirSession.visibilityChecker: FirVisibilityChecker by FirSession.sessionComponentAccessor() fun AbstractFirBasedSymbol<*>.getOwnerId(): ClassId? { diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt index b0802632424..7982410414e 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/FirDeclarationUtil.kt @@ -132,6 +132,13 @@ fun FirRegularClass.addDeclaration(declaration: FirDeclaration) { private object SourceElementKey : FirDeclarationDataKey() var FirRegularClass.sourceElement: SourceElement? by FirDeclarationDataRegistry.data(SourceElementKey) +val FirMemberDeclaration.containerSource: SourceElement? + get() = when (this) { + is FirCallableMemberDeclaration<*> -> containerSource + is FirRegularClass -> sourceElement + else -> null + } + private object IsFromVarargKey : FirDeclarationDataKey() var FirProperty.isFromVararg: Boolean? by FirDeclarationDataRegistry.data(IsFromVarargKey) private object IsReferredViaField : FirDeclarationDataKey() diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/library/a.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/library/a.kt new file mode 100644 index 00000000000..0aba9878eca --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/library/a.kt @@ -0,0 +1,15 @@ +package a + +internal interface InternalInterface + +public class PublicClass internal constructor() { + internal fun internalMemberFun() {} + + internal companion object {} +} + +internal val internalVal = "" + +internal fun internalFun(s: String): String = s + +internal typealias InternalTypealias = InternalInterface diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/output.txt new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/output.txt @@ -0,0 +1 @@ +OK diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/source.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/source.kt new file mode 100644 index 00000000000..5e872880a78 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/internalFromFriendModuleFir/source.kt @@ -0,0 +1,10 @@ +import a.* + +private fun test(i: InternalInterface): InternalTypealias { + PublicClass().internalMemberFun() + PublicClass.Companion + + internalFun(internalVal) + + return i +} diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirModuleInfoProvider.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirModuleInfoProvider.kt index 2b2fd88df6f..b41a7763cf0 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirModuleInfoProvider.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/frontend/fir/FirModuleInfoProvider.kt @@ -27,16 +27,13 @@ class FirModuleInfoProvider( module.dependencies.mapTo(dependencies) { convertToFirModuleInfo(testServices.dependencyProvider.getTestModule(it.moduleName)) } - FirJvmModuleInfo( - module.name, - dependencies - ) + FirJvmModuleInfo(Name.identifier(module.name), dependencies) } } fun builtinsModuleInfoForModule(module: TestModule): FirJvmModuleInfo { return builtinsByModule.getOrPut(module) { - FirJvmModuleInfo(Name.special(""), emptyList()) + FirJvmModuleInfo(Name.special("")) } } } diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt index f6c05bc2bea..d49f6840d2f 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt @@ -626,6 +626,11 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-Xfriend-paths=${library.path}")) } + fun testInternalFromFriendModuleFir() { + val library = compileLibrary("library") + compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-Xfriend-paths=${library.path}", "-Xuse-fir")) + } + fun testJvmDefaultClashWithOld() { val library = compileLibrary("library", additionalOptions = listOf("-Xjvm-default=disable")) compileKotlin("source.kt", tmpdir, listOf(library), additionalOptions = listOf("-jvm-target", "1.8", "-Xjvm-default=all"))