diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 146ba20b872..dc3f661f72d 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope import org.jetbrains.kotlin.ir.builders.irBlock import org.jetbrains.kotlin.ir.builders.irNull import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrFunctionReference @@ -92,6 +93,8 @@ class JvmBackendContext( internal val multifileFacadeForPart = mutableMapOf() internal val multifileFacadeMemberToPartMember = mutableMapOf() + internal val hiddenConstructors = mutableMapOf() + override var inVerbosePhase: Boolean = false override val configuration get() = state.configuration diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt index 7f95abc9517..3c0ac5b9874 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/IrUtils.kt @@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.jvm.JvmSymbols import org.jetbrains.kotlin.backend.jvm.codegen.isInlineOnly import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDeclarationFactory -import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.hasMangledParameters import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.Visibilities @@ -141,10 +140,6 @@ fun IrType.getArrayElementType(irBuiltIns: IrBuiltIns): IrType = val IrStatementOrigin?.isLambda: Boolean get() = this == IrStatementOrigin.LAMBDA || this == IrStatementOrigin.ANONYMOUS_FUNCTION -val IrConstructor.shouldBeHidden: Boolean - get() = !Visibilities.isPrivate(visibility) && !constructedClass.isInline && hasMangledParameters && - origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER - // An IR builder with a reference to the JvmBackendContext class JvmIrBuilder( val backendContext: JvmBackendContext, diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt index 96001357bbf..f369b063f8f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SyntheticAccessorLowering.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.backend.jvm.intrinsics.receiverAndArgs import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator import org.jetbrains.kotlin.backend.jvm.ir.hasJvmDefault import org.jetbrains.kotlin.backend.jvm.ir.isLambda -import org.jetbrains.kotlin.backend.jvm.ir.shouldBeHidden +import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.hasMangledParameters import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility @@ -57,20 +57,30 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle return super.visitFunctionAccess(expression) } + val callee = expression.symbol.owner val withSuper = (expression as? IrCall)?.superQualifierSymbol != null val thisSymbol = (expression as? IrCall)?.dispatchReceiver?.type?.classifierOrNull as? IrClassSymbol - if (expression.symbol.isAccessible(withSuper, thisSymbol)) { - return super.visitFunctionAccess(expression) - } - return super.visitExpression( - modifyFunctionAccessExpression(expression, functionMap.getOrPut(expression.symbol) { - when (val symbol = expression.symbol) { - is IrConstructorSymbol -> symbol.owner.makeConstructorAccessor().symbol - is IrSimpleFunctionSymbol -> symbol.owner.makeSimpleFunctionAccessor(expression as IrCall).symbol - else -> error("Unknown subclass of IrFunctionSymbol") + + val accessor = when { + callee is IrConstructor && callee.isOrShouldBeHidden -> + handleHiddenConstructor(callee).symbol + + !expression.symbol.isAccessible(withSuper, thisSymbol) -> + functionMap.getOrPut(expression.symbol) { + when (val symbol = expression.symbol) { + is IrConstructorSymbol -> symbol.owner.makeConstructorAccessor().also { accessor -> + pendingTransformations.add { + (accessor.parent as IrDeclarationContainer).declarations.add(accessor) + } + }.symbol + is IrSimpleFunctionSymbol -> symbol.owner.makeSimpleFunctionAccessor(expression as IrCall).symbol + else -> error("Unknown subclass of IrFunctionSymbol") + } } - }) - ) + + else -> return super.visitFunctionAccess(expression) + } + return super.visitExpression(modifyFunctionAccessExpression(expression, accessor)) } override fun visitGetField(expression: IrGetField) = super.visitExpression( @@ -90,25 +100,26 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle ) override fun visitConstructor(declaration: IrConstructor): IrStatement { - handleHiddenConstructor(declaration) - return super.visitConstructor(declaration) - } + if (declaration.isOrShouldBeHidden) { + pendingTransformations.add { + declaration.parentAsClass.declarations.add(handleHiddenConstructor(declaration)) + } + declaration.visibility = Visibilities.PRIVATE + } - override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { - handleHiddenConstructor(expression.symbol.owner) - return super.visitConstructorCall(expression) + return super.visitConstructor(declaration) } override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { val function = expression.symbol.owner - if (!expression.origin.isLambda && function is IrConstructor) { - handleHiddenConstructor(function)?.let { accessor -> + if (!expression.origin.isLambda && function is IrConstructor && function.isOrShouldBeHidden) { + handleHiddenConstructor(function).let { accessor -> expression.transformChildrenVoid() return IrFunctionReferenceImpl( expression.startOffset, expression.endOffset, expression.type, - accessor, accessor.owner.typeParameters.size, - accessor.owner.valueParameters.size, expression.origin + accessor.symbol, accessor.typeParameters.size, + accessor.valueParameters.size, expression.origin ) } } @@ -116,28 +127,28 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle return super.visitFunctionReference(expression) } - private fun handleHiddenConstructor(declaration: IrConstructor): IrConstructorSymbol? { - functionMap[declaration.symbol]?.let { return it as IrConstructorSymbol } + private val IrConstructor.isOrShouldBeHidden: Boolean + get() = this in context.hiddenConstructors || ( + !Visibilities.isPrivate(visibility) && !constructedClass.isInline && hasMangledParameters && + origin != IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER && + origin != JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR) - if (!declaration.shouldBeHidden) - return null - - declaration.visibility = Visibilities.PRIVATE - - return declaration.makeConstructorAccessor().also { accessor -> - functionMap[declaration.symbol] = accessor.symbol - - // There's a special case in the JVM backend for serializing the metadata of hidden - // constructors - we serialize the descriptor of the original constructor, but the - // signature of the bridge. We implement this special case in the JVM IR backend by - // attaching the metadata directly to the bridge. We also have to move all annotations - // to the bridge method. Parameter annotations are already moved by the copyTo method. - accessor.metadata = declaration.metadata - declaration.safeAs()?.metadata = null - accessor.annotations += declaration.annotations - declaration.annotations.clear() - declaration.valueParameters.forEach { it.annotations.clear() } - }.symbol + private fun handleHiddenConstructor(declaration: IrConstructor): IrConstructorImpl { + require(declaration.isOrShouldBeHidden, declaration::render) + return context.hiddenConstructors.getOrPut(declaration) { + declaration.makeConstructorAccessor().also { accessor -> + // There's a special case in the JVM backend for serializing the metadata of hidden + // constructors - we serialize the descriptor of the original constructor, but the + // signature of the accessor. We implement this special case in the JVM IR backend by + // attaching the metadata directly to the accessor. We also have to move all annotations + // to the accessor. Parameter annotations are already moved by the copyTo method. + accessor.metadata = declaration.metadata + declaration.safeAs()?.metadata = null + accessor.annotations += declaration.annotations + declaration.annotations.clear() + declaration.valueParameters.forEach { it.annotations.clear() } + } + } } // In case of Java `protected static`, access could be done from a public inline function in the same package, @@ -155,10 +166,8 @@ internal class SyntheticAccessorLowering(val context: JvmBackendContext) : IrEle origin = JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR name = source.name visibility = Visibilities.PUBLIC - }.also { accessor -> accessor.parent = source.parent - pendingTransformations.add { (accessor.parent as IrDeclarationContainer).declarations.add(accessor) } accessor.copyTypeParametersFrom(source, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR) accessor.copyValueParametersToStatic(source, JvmLoweredDeclarationOrigin.SYNTHETIC_ACCESSOR) diff --git a/compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt b/compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt new file mode 100644 index 00000000000..62154a85e65 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt @@ -0,0 +1,10 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: 1.kt + +fun box(): String = X(Z("OK")).z.result + +// FILE: 2.kt + +inline class Z(val result: String) + +class X(val z: Z) diff --git a/compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt b/compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt new file mode 100644 index 00000000000..68592c68740 --- /dev/null +++ b/compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt @@ -0,0 +1,10 @@ +// IGNORE_BACKEND_FIR: JVM_IR +// FILE: 2.kt + +fun box(): String = X(Z("OK")).z.result + +// FILE: 1.kt + +inline class Z(val result: String) + +class X(val z: Z) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index fb870140b31..99d858eedc1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13723,6 +13723,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/hiddenConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } + @TestMetadata("constructorReferencedFromOtherFile1.kt") + public void testConstructorReferencedFromOtherFile1() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt"); + } + + @TestMetadata("constructorReferencedFromOtherFile2.kt") + public void testConstructorReferencedFromOtherFile2() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt"); + } + @TestMetadata("constructorWithDefaultParameters.kt") public void testConstructorWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorWithDefaultParameters.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index f8380ab05bf..def7b4e2ef9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -13723,6 +13723,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/hiddenConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); } + @TestMetadata("constructorReferencedFromOtherFile1.kt") + public void testConstructorReferencedFromOtherFile1() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt"); + } + + @TestMetadata("constructorReferencedFromOtherFile2.kt") + public void testConstructorReferencedFromOtherFile2() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt"); + } + @TestMetadata("constructorWithDefaultParameters.kt") public void testConstructorWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorWithDefaultParameters.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index aee297818f6..ff3adba00b5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -12573,6 +12573,16 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/hiddenConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @TestMetadata("constructorReferencedFromOtherFile1.kt") + public void testConstructorReferencedFromOtherFile1() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt"); + } + + @TestMetadata("constructorReferencedFromOtherFile2.kt") + public void testConstructorReferencedFromOtherFile2() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt"); + } + @TestMetadata("constructorWithDefaultParameters.kt") public void testConstructorWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorWithDefaultParameters.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index ea39d6f1f17..cfe649a8783 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -12573,6 +12573,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/hiddenConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @TestMetadata("constructorReferencedFromOtherFile1.kt") + public void testConstructorReferencedFromOtherFile1() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt"); + } + + @TestMetadata("constructorReferencedFromOtherFile2.kt") + public void testConstructorReferencedFromOtherFile2() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt"); + } + @TestMetadata("constructorWithDefaultParameters.kt") public void testConstructorWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorWithDefaultParameters.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index b47a33f1f0b..21584933cad 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -10888,6 +10888,16 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/hiddenConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); } + @TestMetadata("constructorReferencedFromOtherFile1.kt") + public void testConstructorReferencedFromOtherFile1() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt"); + } + + @TestMetadata("constructorReferencedFromOtherFile2.kt") + public void testConstructorReferencedFromOtherFile2() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt"); + } + @TestMetadata("constructorWithDefaultParameters.kt") public void testConstructorWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorWithDefaultParameters.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index a65f6bb29b6..290ab604107 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -12028,6 +12028,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/inlineClasses/hiddenConstructor"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); } + @TestMetadata("constructorReferencedFromOtherFile1.kt") + public void testConstructorReferencedFromOtherFile1() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile1.kt"); + } + + @TestMetadata("constructorReferencedFromOtherFile2.kt") + public void testConstructorReferencedFromOtherFile2() throws Exception { + runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorReferencedFromOtherFile2.kt"); + } + @TestMetadata("constructorWithDefaultParameters.kt") public void testConstructorWithDefaultParameters() throws Exception { runTest("compiler/testData/codegen/box/inlineClasses/hiddenConstructor/constructorWithDefaultParameters.kt");