[PL] Native: Remove harmful private visibility guard in deserialization of LazyIr

Kotlin/Native codegen needs to deserialize all fields throughout the class hierarchy to build the proper binary class layout. That becomes impossible with the guard condition that prevents loading private top-level classes from another module in LazyIR (see https://github.com/JetBrains/kotlin/blob/2a4d8800374578c1aa9ec9c996b393a98f5a6e3b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt#L701). The guard suits well for the partial linkage needs, but it causes the codegen to fail with `Unbound public symbol IrClassPublicSymbolImpl: [ File '/file/in/the/library.kt' <- private.top.level/ClassDeclaration|null[0] ]` error.

To prevent this the guard is removed. This does not influence the partial linkage in general except for the different error message being generated: `Function 'foo' can not be called: Private function declared in module <A> can not be accessed in module <B>` instead of `Function 'foo' can not be called: No function found for symbol '<symbol>'`.

#KT-54469
This commit is contained in:
Dmitriy Dolovov
2023-03-27 11:00:06 +02:00
parent 681e85eaed
commit 7382b31fdc
7 changed files with 35 additions and 21 deletions
@@ -624,7 +624,7 @@ internal class PartiallyLinkedIrTreePatcher(
return null
// Do the minimal visibility check: Make sure that private declaration is not used outside its declaring entity.
// This should be enough to fix KT-54469 (cases #2 and #3).
// This should be enough to fix KT-54469 (cases #1-#3).
val signature = symbol.signature
if (signature != null
@@ -8,7 +8,6 @@ fun box() = abiTest {
success("PublicTopLevelClass") { PublicTopLevelClass_anyReturnType() }
success("PublicTopLevelClass.PublicToInternalNestedClass") { PublicTopLevelClass_PublicToInternalNestedClass_valueParameter(null) }
successViaException("PublicTopLevelClass.PublicToInternalNestedClass") { PublicTopLevelClass_PublicToInternalNestedClass_returnType() }
// TODO: KT-54469, creating instance of PublicTopLevelClass.PublicToInternalNestedClass in another module should fail.
success("PublicTopLevelClass.PublicToInternalNestedClass") { PublicTopLevelClass_PublicToInternalNestedClass_anyReturnType() }
success("PublicTopLevelClass.PublicToProtectedNestedClass") { PublicTopLevelClass_PublicToProtectedNestedClass_valueParameter(null) }
successViaException("PublicTopLevelClass.PublicToProtectedNestedClass") { PublicTopLevelClass_PublicToProtectedNestedClass_returnType() }
@@ -18,7 +17,6 @@ fun box() = abiTest {
inaccessible("PublicToPrivateNestedClass") { PublicTopLevelClass_PublicToPrivateNestedClass_anyReturnType() }
success("PublicTopLevelClass.PublicToInternalInnerClass") { PublicTopLevelClass_PublicToInternalInnerClass_valueParameter(null) }
successViaException("PublicTopLevelClass.PublicToInternalInnerClass") { PublicTopLevelClass_PublicToInternalInnerClass_returnType() }
// TODO: KT-54469, creating instance of PublicTopLevelClass.PublicToInternalInnerClass in another module should fail.
success("PublicTopLevelClass.PublicToInternalInnerClass") { PublicTopLevelClass_PublicToInternalInnerClass_anyReturnType() }
success("PublicTopLevelClass.PublicToProtectedInnerClass") { PublicTopLevelClass_PublicToProtectedInnerClass_valueParameter(null) }
successViaException("PublicTopLevelClass.PublicToProtectedInnerClass") { PublicTopLevelClass_PublicToProtectedInnerClass_returnType() }
@@ -27,7 +25,6 @@ fun box() = abiTest {
successViaException("PublicTopLevelClass.PublicToPrivateInnerClass") { PublicTopLevelClass_PublicToPrivateInnerClass_returnType() }
inaccessible("PublicToPrivateInnerClass") { PublicTopLevelClass_PublicToPrivateInnerClass_anyReturnType() }
// TODO: KT-54469, accessing PublicToInternalTopLevelClass and all nested classes should fail.
success("PublicToInternalTopLevelClass") { PublicToInternalTopLevelClass_valueParameter(null) }
successViaException("PublicToInternalTopLevelClass") { PublicToInternalTopLevelClass_returnType() }
success("PublicToInternalTopLevelClass") { PublicToInternalTopLevelClass_anyReturnType() }
@@ -73,7 +70,6 @@ fun box() = abiTest {
unlinkedConstructorSymbol("/PublicToPrivateTopLevelClass.<init>") { PublicToPrivateTopLevelClass_PublicToPrivateInnerClass_anyReturnType() }
success("PublicTopLevelClassInheritor") { PublicTopLevelClassInheritor() }
// TODO: KT-54469, creating instance of PublicToInternalTopLevelClassInheritor should fail.
success("PublicToInternalTopLevelClassInheritor") { PublicToInternalTopLevelClassInheritor() }
expectFailure(linkage("Constructor 'PublicToPrivateTopLevelClassInheritor.<init>' can not be called: Class 'PublicToPrivateTopLevelClassInheritor' uses unlinked class symbol '/PublicToPrivateTopLevelClass'")) { PublicToPrivateTopLevelClassInheritor() }
}
@@ -8,14 +8,12 @@ fun box() = abiTest {
success("publicToInternalTopLevelFunction.v2") { publicToInternalTopLevelFunction() } // Signature remains the same.
success("publicToInternalPATopLevelFunction.v2") { publicToInternalPATopLevelFunction() } // Signature remains the same.
unlinkedSymbol("/publicToPrivateTopLevelFunction") { publicToPrivateTopLevelFunction() } // Signature changed.
unlinkedTopLevelPrivateSymbol("/publicToPrivateTopLevelFunction") { publicToPrivateTopLevelFunction() } // Signature changed.
success("Container.publicToProtectedFunction.v2") { c.publicToProtectedFunction() } // Signature remains the same.
success("Container.publicToProtectedFunction.v2") { ci.publicToProtectedFunction() } // Signature remains the same.
// TODO: KT-54469, Container.publicToInternalFunction() should fail because it is accessed from another module.
success("Container.publicToInternalFunction.v2") { c.publicToInternalFunction() } // Signature remains the same.
unlinkedSymbol("/ContainerImpl.publicToInternalFunction") { ci.publicToInternalFunction() } // FOs are not generated for internal members from other module.
// TODO: KT-54469, Container.publicToInternalPAFunction() should fail because it is accessed from another module.
success("Container.publicToInternalPAFunction.v2") { c.publicToInternalPAFunction() } // Signature remains the same.
unlinkedSymbol("/ContainerImpl.publicToInternalPAFunction") { ci.publicToInternalPAFunction() } // FOs are not generated for internal members from other module.
inaccessible("publicToPrivateFunction") { c.publicToPrivateFunction() } // Inaccessible from other module though signature remains the same.
@@ -59,6 +57,14 @@ private inline fun TestBuilder.unlinkedSymbol(signature: String, noinline block:
expectFailure(linkage("Function '$functionName' can not be called: No function found for symbol '$signature'"), block)
}
private inline fun TestBuilder.unlinkedTopLevelPrivateSymbol(signature: String, noinline block: () -> Unit) {
if (testMode == NATIVE_CACHE_STATIC_EVERYWHERE) {
val functionName = signature.removePrefix("/").substringAfterLast(".")
expectFailure(linkage("Function '$functionName' can not be called: Private function declared in module <lib1> can not be accessed in module <main>"), block)
} else
unlinkedSymbol(signature, block)
}
private inline fun TestBuilder.inaccessible(functionName: String, noinline block: () -> Unit) = expectFailure(
linkage("Function '$functionName' can not be called: Private function declared in module <lib1> can not be accessed in module <main>"),
block
@@ -10,23 +10,19 @@ fun box() = abiTest {
success("publicToInternalTopLevelProperty2.v2") { publicToInternalTopLevelProperty2 } // Signature remains the same.
success("publicToInternalPATopLevelProperty1.v2") { publicToInternalPATopLevelProperty1 } // Signature remains the same.
success("publicToInternalPATopLevelProperty2.v2") { publicToInternalPATopLevelProperty2 } // Signature remains the same.
unlinkedSymbol("/publicToPrivateTopLevelProperty1.<get-publicToPrivateTopLevelProperty1>") { publicToPrivateTopLevelProperty1 } // Signature changed.
unlinkedSymbol("/publicToPrivateTopLevelProperty2.<get-publicToPrivateTopLevelProperty2>") { publicToPrivateTopLevelProperty2 } // Signature changed.
unlinkedTopLevelPrivateSymbol("/publicToPrivateTopLevelProperty1.<get-publicToPrivateTopLevelProperty1>") { publicToPrivateTopLevelProperty1 } // Signature changed.
unlinkedTopLevelPrivateSymbol("/publicToPrivateTopLevelProperty2.<get-publicToPrivateTopLevelProperty2>") { publicToPrivateTopLevelProperty2 } // Signature changed.
success("Container.publicToProtectedProperty1.v2") { c.publicToProtectedProperty1 } // Signature remains the same.
success("Container.publicToProtectedProperty1.v2") { ci.publicToProtectedProperty1 } // Signature remains the same.
success("Container.publicToProtectedProperty2.v2") { c.publicToProtectedProperty2 } // Signature remains the same.
success("Container.publicToProtectedProperty2.v2") { ci.publicToProtectedProperty2 } // Signature remains the same.
// TODO: KT-54469, Container.publicToInternalProperty1 should fail because it is accessed from another module.
success("Container.publicToInternalProperty1.v2") { c.publicToInternalProperty1 } // Signature remains the same.
unlinkedSymbol("/ContainerImpl.publicToInternalProperty1.<get-publicToInternalProperty1>") { ci.publicToInternalProperty1 } // FOs are not generated for internal members from other module.
// TODO: KT-54469, Container.publicToInternalProperty2 should fail because it is accessed from another module.
success("Container.publicToInternalProperty2.v2") { c.publicToInternalProperty2 } // Signature remains the same.
unlinkedSymbol("/ContainerImpl.publicToInternalProperty2.<get-publicToInternalProperty2>") { ci.publicToInternalProperty2 } // FOs are not generated for internal members from other module.
// TODO: KT-54469, Container.publicToInternalPAProperty1 should fail because it is accessed from another module.
success("Container.publicToInternalPAProperty1.v2") { c.publicToInternalPAProperty1 } // Signature remains the same.
unlinkedSymbol("/ContainerImpl.publicToInternalPAProperty1.<get-publicToInternalPAProperty1>") { ci.publicToInternalPAProperty1 } // FOs are not generated for internal members from other module.
// TODO: KT-54469, Container.publicToInternalPAProperty2 should fail because it is accessed from another module.
success("Container.publicToInternalPAProperty2.v2") { c.publicToInternalPAProperty2 } // Signature remains the same.
unlinkedSymbol("/ContainerImpl.publicToInternalPAProperty2.<get-publicToInternalPAProperty2>") { ci.publicToInternalPAProperty2 } // FOs are not generated for internal members from other module.
inaccessible("publicToPrivateProperty1.<get-publicToPrivateProperty1>") { c.publicToPrivateProperty1 } // Inaccessible from other module though signature remains the same.
@@ -127,6 +123,14 @@ private inline fun TestBuilder.unlinkedSymbol(signature: String, noinline block:
expectFailure(linkage("Property accessor '$accessorName' can not be called: No property accessor found for symbol '$signature'"), block)
}
private inline fun TestBuilder.unlinkedTopLevelPrivateSymbol(signature: String, noinline block: () -> Unit) {
if (testMode == NATIVE_CACHE_STATIC_EVERYWHERE) {
val accessorName = signature.removePrefix("/").split('.').takeLast(2).joinToString(".")
expectFailure(linkage("Property accessor '$accessorName' can not be called: Private property accessor declared in module <lib1> can not be accessed in module <main>"), block)
} else
unlinkedSymbol(signature, block)
}
private inline fun TestBuilder.inaccessible(accessorName: String, noinline block: () -> Unit) = expectFailure(
linkage("Property accessor '$accessorName' can not be called: Private property accessor declared in module <lib1> can not be accessed in module <main>"),
block
@@ -242,3 +242,16 @@ enum class StableEnum {
abstract val test: String
}
// This is required to check that the guard condition initially added in commit
// https://github.com/JetBrains/kotlin/blob/2a4d8800374578c1aa9ec9c996b393a98f5a6e3b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt#L701
// does not break Native codegen anymore.
class StableInheritorOfClassThatUsesPrivateTopLevelClass : AbstractIterator<String>() {
private var i = 0
public override fun computeNext() {
if (i < 5) setNext((i++).toString()) else done()
}
}
fun testStableInheritorOfClassThatUsesPrivateTopLevelClass(): String = buildString {
for (s in StableInheritorOfClassThatUsesPrivateTopLevelClass()) append(s)
}
@@ -210,4 +210,6 @@ fun box() = abiTest {
expectSuccess { StableEnum.FOO.test }
expectSuccess { StableEnum.BAR.test }
expectSuccess("01234") { testStableInheritorOfClassThatUsesPrivateTopLevelClass() }
}
@@ -698,13 +698,6 @@ internal class KonanIrLinker(
deserializedSymbols[idSig]?.let { return it }
val descriptor = descriptorByIdSignatureFinder.findDescriptorBySignature(idSig) ?: return null
if (partialLinkageSupport.isEnabled
&& descriptor.isTopLevelInPackage()
&& (descriptor as? DeclarationDescriptorWithVisibility)?.visibility == DescriptorVisibilities.PRIVATE
&& with(KonanManglerDesc) { !descriptor.isPlatformSpecificExport() }
) {
return null // Fixes case #1 in KT-54469
}
descriptorSignatures[descriptor] = idSig