[KLIB stdlib] Test visibility of intrinsics that can be used in KLIBs with inlined IR

^KT-69947 Fixed
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/CollectNotVisibleFromOriginalModuleIrVisitor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/CollectNotVisibleFromOriginalModuleIrVisitor.kt
new file mode 100644
index 0000000..033036b
--- /dev/null
+++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/CollectNotVisibleFromOriginalModuleIrVisitor.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010-2025 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.ir.util
+
+import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
+import org.jetbrains.kotlin.ir.IrElement
+import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.expressions.IrCall
+import org.jetbrains.kotlin.ir.visitors.IrVisitorVoid
+import org.jetbrains.kotlin.name.FqName
+
+class CollectNotVisibleFromOriginalModuleIrVisitor(
+    private val notVisible: MutableList<IrSimpleFunction>,
+    private val originalModule: IrModuleFragment,
+) : IrVisitorVoid() {
+    override fun visitElement(element: IrElement) {
+        element.acceptChildren(this, null)
+    }
+
+    // TODO: Not just calls
+    override fun visitCall(expression: IrCall) {
+        val functionDeclaration = expression.symbol.owner
+        val module = functionDeclaration.fileOrNull?.module ?: return
+        if (originalModule == module && !isVisible(functionDeclaration)) {
+            notVisible.add(functionDeclaration)
+        }
+        super.visitCall(expression)
+    }
+
+    private fun isVisible(functionDeclaration: IrSimpleFunction): Boolean {
+        return functionDeclaration.fqNameWhenAvailable in FQ_NAMES_EXCLUDED_FROM_VISIBILITY_CHECKS ||
+                functionDeclaration.visibility == DescriptorVisibilities.PUBLIC ||
+                (functionDeclaration.visibility == DescriptorVisibilities.INTERNAL &&
+                        functionDeclaration.annotations.hasAnnotation(publishedApiAnnotation))
+    }
+
+    private val publishedApiAnnotation = FqName("kotlin.PublishedApi")
+
+    // TODO: Discuss relation between KT-70295 and KT-69947
+    private val FQ_NAMES_EXCLUDED_FROM_VISIBILITY_CHECKS: Set<FqName> = listOf(
+        "kotlin.js.sharedBoxCreate",
+        "kotlin.js.sharedBoxWrite",
+        "kotlin.js.sharedBoxRead",
+        "kotlin.wasm.internal.ClosureBoxBoolean",
+        "kotlin.wasm.internal.ClosureBoxByte",
+        "kotlin.wasm.internal.ClosureBoxShort",
+        "kotlin.wasm.internal.ClosureBoxChar",
+        "kotlin.wasm.internal.ClosureBoxInt",
+        "kotlin.wasm.internal.ClosureBoxLong",
+        "kotlin.wasm.internal.ClosureBoxFloat",
+        "kotlin.wasm.internal.ClosureBoxDouble",
+        "kotlin.wasm.internal.ClosureBoxAny",
+        "kotlin.wasm.internal.wasmTypeId",
+        "kotlin.coroutines.CoroutineImpl",
+        "kotlin.native.internal.KClassImpl",
+        "kotlin.native.internal.KTypeImpl",
+        "kotlin.native.internal.KTypeProjectionList",
+        "kotlin.native.internal.KTypeParameterImpl",
+    ).mapTo(hashSetOf(), ::FqName)
+}
\ No newline at end of file
diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/StdlibVisibilityIrHandler.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/StdlibVisibilityIrHandler.kt
new file mode 100644
index 0000000..9a13acc
--- /dev/null
+++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/backend/handlers/StdlibVisibilityIrHandler.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010-2025 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.test.backend.handlers
+
+import org.jetbrains.kotlin.ir.IrElement
+import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
+import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
+import org.jetbrains.kotlin.ir.declarations.name
+import org.jetbrains.kotlin.ir.util.CollectNotVisibleFromOriginalModuleIrVisitor
+import org.jetbrains.kotlin.ir.util.file
+import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
+import org.jetbrains.kotlin.test.backend.ir.IrBackendInput
+import org.jetbrains.kotlin.test.model.TestModule
+import org.jetbrains.kotlin.test.services.TestServices
+
+class StdlibVisibilityIrHandler(testServices: TestServices) : AbstractIrHandler(testServices) {
+    override fun processModule(module: TestModule, info: IrBackendInput) {
+        val stdlibModule = info.irPluginContext.irBuiltIns.anyClass.owner.file.module
+        val irFiles = info.irModuleFragment.files.sortedBy { it.name }
+        val notVisibleElements = irFiles.flatMap { collectNotVisibleElements(it, stdlibModule) }
+        assert(notVisibleElements.isEmpty()) { "Found not visible elements ${notVisibleElements.toSet().map { it.fqNameWhenAvailable }}" }
+    }
+
+    override fun processAfterAllModules(someAssertionWasFailed: Boolean) {}
+
+    private fun collectNotVisibleElements(element: IrElement, module: IrModuleFragment): List<IrSimpleFunction> =
+        buildList<IrSimpleFunction> {
+            element.accept(CollectNotVisibleFromOriginalModuleIrVisitor(this, module), null)
+        }
+}
\ No newline at end of file
diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsBlackBoxCodegenTestBase.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsBlackBoxCodegenTestBase.kt
index 512deb8..aa28c93 100644
--- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsBlackBoxCodegenTestBase.kt
+++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/AbstractJsBlackBoxCodegenTestBase.kt
@@ -154,7 +154,7 @@
             is JsBackendFacades.WithSeparatedDeserialization -> {
                 configureInlinedIrHandlersStep { useHandlers(backendFacades.preSerializationHandler) }
                 facadeStep(backendFacades.deserializerFacade)
-                deserializedIrHandlersStep { useHandlers(backendFacades.postDeserializationHandler) }
+                deserializedIrHandlersStep { useHandlers(backendFacades.postDeserializationHandler, ::StdlibVisibilityIrHandler) }
             }
         }
 
diff --git a/native/native.tests/klib-ir-inliner/tests/org/jetbrains/kotlin/konan/test/serialization/AbstractNativeIrDeserializationTest.kt b/native/native.tests/klib-ir-inliner/tests/org/jetbrains/kotlin/konan/test/serialization/AbstractNativeIrDeserializationTest.kt
index 12dde88..4e267fe 100644
--- a/native/native.tests/klib-ir-inliner/tests/org/jetbrains/kotlin/konan/test/serialization/AbstractNativeIrDeserializationTest.kt
+++ b/native/native.tests/klib-ir-inliner/tests/org/jetbrains/kotlin/konan/test/serialization/AbstractNativeIrDeserializationTest.kt
@@ -14,6 +14,7 @@
 import org.jetbrains.kotlin.test.FirParser
 import org.jetbrains.kotlin.test.TargetBackend
 import org.jetbrains.kotlin.test.backend.BlackBoxCodegenSuppressor
+import org.jetbrains.kotlin.test.backend.handlers.StdlibVisibilityIrHandler
 import org.jetbrains.kotlin.test.backend.handlers.IrMangledNameAndSignatureDumpHandler
 import org.jetbrains.kotlin.test.backend.handlers.KlibAbiDumpHandler
 import org.jetbrains.kotlin.test.backend.handlers.KlibBackendDiagnosticsHandler
@@ -173,5 +174,5 @@
     klibArtifactsHandlersStep {
         useHandlers(::KlibBackendDiagnosticsHandler)
     }
-    deserializedIrHandlersStep { useHandlers({ SerializedIrDumpHandler(it, isAfterDeserialization = true) }) }
+    deserializedIrHandlersStep { useHandlers({ SerializedIrDumpHandler(it, isAfterDeserialization = true)}, ::StdlibVisibilityIrHandler ) }
 }