[FIR+JVM_IR+stdlib] Prototype of @JvmExposeBoxed implementation
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt index dbc280a..317e890 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDataClassConverters.kt
@@ -4519,6 +4519,12 @@ token, ) } + add(FirJvmErrors.JVM_EXPOSE_BOXED_WITHOUT_INLINE) { firDiagnostic -> + JvmExposeBoxedWithoutInlineImpl( + firDiagnostic as KtPsiDiagnostic, + token, + ) + } add(FirJvmErrors.JAVA_TYPE_MISMATCH) { firDiagnostic -> JavaTypeMismatchImpl( firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt index 282bc75..568af08 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnostics.kt
@@ -3149,6 +3149,10 @@ override val diagnosticClass get() = JvmInlineWithoutValueClass::class } + interface JvmExposeBoxedWithoutInline : KtFirDiagnostic<KtAnnotationEntry> { + override val diagnosticClass get() = JvmExposeBoxedWithoutInline::class + } + interface JavaTypeMismatch : KtFirDiagnostic<KtExpression> { override val diagnosticClass get() = JavaTypeMismatch::class val expectedType: KtType
diff --git a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 6aa4baa..d16bce3 100644 --- a/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/analysis/analysis-api-fir/src/org/jetbrains/kotlin/analysis/api/fir/diagnostics/KtFirDiagnosticsImpl.kt
@@ -3801,6 +3801,11 @@ token: KtLifetimeToken, ) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.JvmInlineWithoutValueClass +internal class JvmExposeBoxedWithoutInlineImpl( + firDiagnostic: KtPsiDiagnostic, + token: KtLifetimeToken, +) : KtAbstractFirDiagnostic<KtAnnotationEntry>(firDiagnostic, token), KtFirDiagnostic.JvmExposeBoxedWithoutInline + internal class JavaTypeMismatchImpl( override val expectedType: KtType, override val actualType: KtType,
diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt index 7dfa092..30bfd94 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirJvmDiagnosticsList.kt
@@ -40,6 +40,7 @@ val VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION by error<PsiElement>() val JVM_INLINE_WITHOUT_VALUE_CLASS by error<PsiElement>() + val JVM_EXPOSE_BOXED_WITHOUT_INLINE by error<KtAnnotationEntry>() } val TYPES by object : DiagnosticGroup("Types") {
diff --git a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt index 744b105..a98a768 100644 --- a/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt +++ b/compiler/fir/checkers/checkers.jvm/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrors.kt
@@ -44,6 +44,7 @@ val FUNCTION_DELEGATE_MEMBER_NAME_CLASH by error0<PsiElement>(SourceElementPositioningStrategies.DECLARATION_NAME) val VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION by error0<PsiElement>() val JVM_INLINE_WITHOUT_VALUE_CLASS by error0<PsiElement>() + val JVM_EXPOSE_BOXED_WITHOUT_INLINE by error0<KtAnnotationEntry>() // Types val JAVA_TYPE_MISMATCH by error2<KtExpression, ConeKotlinType, ConeKotlinType>()
diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt index 629614b..c11463f 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/diagnostics/jvm/FirJvmErrorsDefaultMessages.kt
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_DEFAULT_IN_DECLARATION import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_DEFAULT_WITH_COMPATIBILITY_IN_DECLARATION import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_DEFAULT_WITH_COMPATIBILITY_NOT_ON_INTERFACE +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_EXPOSE_BOXED_WITHOUT_INLINE import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_INLINE_WITHOUT_VALUE_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_PACKAGE_NAME_CANNOT_BE_EMPTY import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors.JVM_PACKAGE_NAME_MUST_BE_VALID_NAME @@ -161,6 +162,7 @@ map.put(VALUE_CLASS_WITHOUT_JVM_INLINE_ANNOTATION, "Value classes without @JvmInline annotation are not supported yet") map.put(JVM_INLINE_WITHOUT_VALUE_CLASS, "@JvmInline annotation is only applicable to value classes") + map.put(JVM_EXPOSE_BOXED_WITHOUT_INLINE, "'@JvmExposeBoxed' annotation is only applicable to '@JvmInline' value classes") map.put(JVM_DEFAULT_IN_DECLARATION, "Usage of ''@{0}'' is only allowed with -Xjvm-default option", STRING) map.put(
diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt index 824168a..e5144f9 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmDeclarationCheckers.kt
@@ -30,6 +30,7 @@ get() = setOf( FirJvmRecordChecker, FirJvmInlineApplicabilityChecker, + FirJvmExposeBoxedChecker, FirJvmConflictsChecker, )
diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmExposeBoxedChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmExposeBoxedChecker.kt new file mode 100644 index 0000000..e5331f2 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/declaration/FirJvmExposeBoxedChecker.kt
@@ -0,0 +1,27 @@ +/* + * 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.analysis.jvm.checkers.declaration + +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirRegularClassChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.jvm.FirJvmErrors +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.* +import org.jetbrains.kotlin.resolve.JVM_EXPOSE_BOXED_ANNOTATION_CLASS_ID +import org.jetbrains.kotlin.resolve.JVM_INLINE_ANNOTATION_CLASS_ID + +object FirJvmExposeBoxedChecker : FirRegularClassChecker() { + override fun check(declaration: FirRegularClass, context: CheckerContext, reporter: DiagnosticReporter) { + val jvmInlineAnnotation = declaration.getAnnotationByClassId(JVM_INLINE_ANNOTATION_CLASS_ID, context.session) + val jvmExposeBoxedAnnotation = declaration.getAnnotationByClassId(JVM_EXPOSE_BOXED_ANNOTATION_CLASS_ID, context.session) + + if ((jvmInlineAnnotation == null || !declaration.isInline) && jvmExposeBoxedAnnotation != null) { + reporter.reportOn(jvmExposeBoxedAnnotation.source, FirJvmErrors.JVM_EXPOSE_BOXED_WITHOUT_INLINE, context) + } + } +}
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java index 5b35dc2..c7a1155 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirLightTreeBlackBoxCodegenTestGenerated.java
@@ -52440,6 +52440,12 @@ } @Test + @TestMetadata("exposedInlineClass.kt") + public void testExposedInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/exposedInlineClass.kt"); + } + + @Test @TestMetadata("fakeOverrideCall.kt") public void testFakeOverrideCall() throws Exception { runTest("compiler/testData/codegen/box/valueClasses/fakeOverrideCall.kt");
diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java index ca7a1d9..2f832a7 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirPsiBlackBoxCodegenTestGenerated.java
@@ -52440,6 +52440,12 @@ } @Test + @TestMetadata("exposedInlineClass.kt") + public void testExposedInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/exposedInlineClass.kt"); + } + + @Test @TestMetadata("fakeOverrideCall.kt") public void testFakeOverrideCall() throws Exception { runTest("compiler/testData/codegen/box/valueClasses/fakeOverrideCall.kt");
diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index ce76bba..de50875 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt
@@ -251,14 +251,14 @@ } val contextReceivers = valueParameters.subList(0, contextReceiverParametersCount) for (contextReceiver in contextReceivers) { - frameMap.enter(contextReceiver, classCodegen.typeMapper.mapType(contextReceiver.type)) + frameMap.enter(contextReceiver, classCodegen.typeMapper.mapType(contextReceiver)) } extensionReceiverParameter?.let { frameMap.enter(it, classCodegen.typeMapper.mapType(it)) } val regularParameters = valueParameters.subList(contextReceiverParametersCount, valueParameters.size) for (parameter in regularParameters) { - frameMap.enter(parameter, classCodegen.typeMapper.mapType(parameter.type)) + frameMap.enter(parameter, classCodegen.typeMapper.mapType(parameter)) } return frameMap }
diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt index 771cee6..14ae87f 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/intrinsics/CompareTo.kt
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.jvm.codegen.* import org.jetbrains.kotlin.backend.jvm.ir.isSmartcastFromHigherThanNullable import org.jetbrains.kotlin.backend.jvm.ir.receiverAndArgs +import org.jetbrains.kotlin.backend.jvm.mapping.mapType import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType @@ -58,7 +59,7 @@ val callee = expression.symbol.owner val calleeParameter = callee.dispatchReceiverParameter ?: callee.extensionReceiverParameter!! val parameterType = comparisonOperandType( - classCodegen.typeMapper.mapType(calleeParameter.type), + classCodegen.typeMapper.mapType(calleeParameter), signature.valueParameters.single().asmType, ) return IrIntrinsicFunction.create(expression, signature, classCodegen, listOf(parameterType, parameterType)) {
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt index 0a8cc10..7254593 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/BridgeLowering.kt
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements import org.jetbrains.kotlin.backend.jvm.SpecialBridge import org.jetbrains.kotlin.backend.jvm.ir.* +import org.jetbrains.kotlin.backend.jvm.mapping.mapType import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality @@ -552,7 +553,7 @@ // If the signature of this method will be changed in the output to take a boxed argument instead of a primitive, // rewrite the argument so that code will be generated for a boxed argument and not a primitive. valueParameters.forEachIndexed { i, p -> - if (AsmUtil.isPrimitive(context.defaultTypeMapper.mapType(p.type)) && ourSignature.argumentTypes[i].sort == Type.OBJECT) { + if (AsmUtil.isPrimitive(context.defaultTypeMapper.mapType(p)) && ourSignature.argumentTypes[i].sort == Type.OBJECT) { p.type = p.type.makeNullable() } }
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt index 226f86f..a46e340 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInlineClassLowering.kt
@@ -8,16 +8,21 @@ import org.jetbrains.kotlin.backend.common.ScopeWithIr import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.common.lower.irBlockBody +import org.jetbrains.kotlin.backend.common.pop +import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.jvm.* import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound +import org.jetbrains.kotlin.backend.jvm.ir.isExposedSingleFieldValueClass import org.jetbrains.kotlin.builtins.StandardNames import org.jetbrains.kotlin.config.ApiVersion import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.addConstructor import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* @@ -71,6 +76,7 @@ override val specificMangle: SpecificMangle get() = SpecificMangle.Inline + override fun visitClassNewDeclarationsWhenParallel(declaration: IrDeclaration) = Unit override fun visitClassNew(declaration: IrClass): IrClass { @@ -99,12 +105,13 @@ } override fun handleSpecificNewClass(declaration: IrClass) { - val irConstructor = declaration.primaryConstructor!! + val primaryConstructor = declaration.primaryConstructor!! // The field getter is used by reflection and cannot be removed here unless it is internal. - declaration.declarations.removeIf { - it == irConstructor || (it is IrFunction && it.isInlineClassFieldGetter && !it.visibility.isPublicAPI) + declaration.declarations.removeAll { + it == primaryConstructor || (it is IrFunction && it.isInlineClassFieldGetter && !it.visibility.isPublicAPI) } - buildPrimaryInlineClassConstructor(declaration, irConstructor) + + buildInlineClassConstructors(declaration, primaryConstructor) buildBoxFunction(declaration) buildUnboxFunction(declaration) buildSpecializedEqualsMethodIfNeeded(declaration) @@ -131,11 +138,82 @@ } } + override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> { + replacement.valueParameters.forEach { + visitParameter(it) + it.defaultValue?.patchDeclarationParents(replacement) + } + + allScopes.push(createScope(replacement)) + replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement) + allScopes.pop() + replacement.copyAttributes(function) + val result = ArrayList<IrDeclaration>(3) + result += replacement + + val declaredOverrideWithDispatchReceiver = function.overriddenSymbols.isNotEmpty() && replacement.dispatchReceiverParameter == null + + val takesOrReturnsExposeBoxedClass = function.explicitParameters + .any { it.type.classOrNull?.owner?.isExposedSingleFieldValueClass == true } + || function.returnType.classOrNull?.owner?.isExposedSingleFieldValueClass == true + + if (declaredOverrideWithDispatchReceiver) + result += createBridgeFunction(function, replacement) + + // Replaces the original with function taking/returning boxed inline class, intended for use from Java. + // The function body just calls the replacement. + if (takesOrReturnsExposeBoxedClass && !function.isEquals() && !function.isToString() && !function.isHashCode()) { + function.origin = JvmLoweredDeclarationOrigin.FUNCTION_WITH_EXPOSED_INLINE_CLASS + + // Don't create a default argument stub + for (param in function.valueParameters) { + param.defaultValue = null + } + + if (replacement.modality != Modality.ABSTRACT) { + function.body = context.createIrBuilder(function.symbol).irBlockBody(function) { + +irReturn(irCall(replacement).also { access -> + var paramOffset = 0 + + if (function.parentClassOrNull?.isExposedSingleFieldValueClass == true) { + function.dispatchReceiverParameter?.let { + access.putValueArgument(paramOffset, irGet(it)) + paramOffset++ + } + + function.extensionReceiverParameter?.let { + access.putValueArgument(paramOffset, irGet(it)) + paramOffset++ + } + } else { + function.dispatchReceiverParameter?.let { + access.dispatchReceiver = irGet(it) + } + + function.extensionReceiverParameter?.let { + access.extensionReceiver = irGet(it) + } + } + + for ((idx, parameter) in function.valueParameters.withIndex()) { + access.putValueArgument(paramOffset + idx, irGet(parameter)) + } + }) + } + } + + result += function + } + + return result + } + // Secondary constructors for boxed types get translated to static functions returning // unboxed arguments. We remove the original constructor. // Primary constructors' case is handled at the start of transformFunctionFlat override fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> { replacement.valueParameters.forEach { it.transformChildrenVoid() } + replacement.body = context.createIrBuilder(replacement.symbol, replacement.startOffset, replacement.endOffset).irBlockBody( replacement ) { @@ -146,9 +224,7 @@ +statement .transformStatement(object : IrElementTransformerVoid() { // Don't recurse under nested class declarations - override fun visitClass(declaration: IrClass): IrStatement { - return declaration - } + override fun visitClass(declaration: IrClass): IrStatement = declaration // Capture the result of a delegating constructor call in a temporary variable "thisVar". // @@ -189,13 +265,49 @@ +irReturn(irGet(thisVar)) } + if (constructor.parentAsClass.isExposedSingleFieldValueClass) { + val valueClass = constructor.parentAsClass + + constructor.valueParameters = constructor.valueParameters.map { + it.deepCopyWithSymbols(initialParent = constructor) + } + + constructor.body = context.createIrBuilder(constructor.symbol).irBlockBody(constructor) { + +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) + val firstArgument = constructor.valueParameters[0] + val field = getInlineClassBackingField(valueClass) + + +irSetField( + irGet(valueClass.thisReceiver!!), + field, + coerceInlineClasses( + irCall(replacement.symbol).also { access -> + access.putValueArgument(0, irGet(firstArgument)) + val valueParameterMap = constructor.explicitParameters.zip(replacement.explicitParameters).toMap() + + for ((parameter, argument) in typedArgumentList(constructor, access)) { + if (argument == null) continue + val newParameter = valueParameterMap.getValue(parameter) + access.putArgument(replacement, newParameter, argument.transform(this@JvmInlineClassLowering, null)) + } + }, + replacement.returnType, + field.type, + true, + ) + ) + } + + return listOf(constructor, replacement) + } + return listOf(replacement) } private fun IrMemberAccessExpression<*>.buildReplacement( originalFunction: IrFunction, original: IrMemberAccessExpression<*>, - replacement: IrSimpleFunction + replacement: IrSimpleFunction, ) { copyTypeArgumentsFrom(original) val valueParameterMap = originalFunction.explicitParameters.zip(replacement.explicitParameters).toMap() @@ -347,8 +459,7 @@ .specializeEqualsCall(leftOp, rightOp) ?: expression } - else -> - super.visitCall(expression) + else -> super.visitCall(expression) } private val IrCall.isEqualsMethodCallOnInlineClass: Boolean @@ -413,59 +524,110 @@ return super.visitSetValue(expression) } - private fun buildPrimaryInlineClassConstructor(valueClass: IrClass, irConstructor: IrConstructor) { - // Add the default primary constructor + private fun buildInlineClassConstructors(valueClass: IrClass, constructor: IrConstructor) { + // Add the primary synthetic constructor for boxing valueClass.addConstructor { - updateFrom(irConstructor) + updateFrom(constructor) visibility = DescriptorVisibilities.PRIVATE origin = JvmLoweredDeclarationOrigin.SYNTHETIC_INLINE_CLASS_MEMBER - returnType = irConstructor.returnType + returnType = constructor.returnType }.apply { - // Don't create a default argument stub for the primary constructor - irConstructor.valueParameters.forEach { it.defaultValue = null } - copyParameterDeclarationsFrom(irConstructor) - annotations = irConstructor.annotations - body = context.createIrBuilder(this.symbol).irBlockBody(this) { + copyParameterDeclarationsFrom(constructor) + + // Don't create a default argument stub for the primary constructor for boxing + for (param in valueParameters) { + param.defaultValue = null + } + + if (valueClass.isExposedSingleFieldValueClass) { + valueParameters += buildValueParameter(this) { + type = context.irBuiltIns.nothingNType + name = Name.identifier("boxingConstructorMarker") + index = 1 + } + } + annotations = constructor.annotations + body = context.createIrBuilder(symbol).irBlockBody(this) { +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) +irSetField( irGet(valueClass.thisReceiver!!), getInlineClassBackingField(valueClass), - irGet(this@apply.valueParameters[0]) + irGet(this@apply.valueParameters[0]), ) } } // Add a static bridge method to the primary constructor. This contains // null-checks, default arguments, and anonymous initializers. - val function = context.inlineClassReplacements.getReplacementFunction(irConstructor)!! + val constructorImpl = checkNotNull(context.inlineClassReplacements.getReplacementFunction(constructor)) { + "No replacement function present for ${constructor.dump()}" + } val initBlocks = valueClass.declarations.filterIsInstance<IrAnonymousInitializer>() .filterNot { it.isStatic } - function.valueParameters.forEach { it.transformChildrenVoid() } - function.body = context.createIrBuilder(function.symbol).irBlockBody { - val argument = function.valueParameters[0] - val thisValue = irTemporary(coerceInlineClasses(irGet(argument), argument.type, function.returnType, skipCast = true)) + constructorImpl.valueParameters.forEach { it.transformChildrenVoid() } + constructorImpl.body = context.createIrBuilder(constructorImpl.symbol).irBlockBody { + val argument = constructorImpl.valueParameters[0] + val thisValue = irTemporary(coerceInlineClasses(irGet(argument), argument.type, constructorImpl.returnType, skipCast = true)) valueMap[valueClass.thisReceiver!!.symbol] = thisValue for (initBlock in initBlocks) { for (stmt in initBlock.body.statements) { - +stmt.transformStatement(this@JvmInlineClassLowering).patchDeclarationParents(function) + +stmt.transformStatement(this@JvmInlineClassLowering).patchDeclarationParents(constructorImpl) } } +irReturn(irGet(thisValue)) } + if (valueClass.isExposedSingleFieldValueClass) { + // Build primary constructor as a public secondary constructor for Java + valueClass.addConstructor { + updateFrom(constructor) + returnType = constructor.returnType + isPrimary = false + }.apply constructor@{ + copyParameterDeclarationsFrom(constructor) + copyAnnotationsFrom(constructor) + + // Don't create a default argument stub + for (param in valueParameters) { + param.defaultValue = null + } + + body = context.createIrBuilder(symbol).irBlockBody(this) { + +irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single()) + +irSetField( + irGet(valueClass.thisReceiver!!), + getInlineClassBackingField(valueClass), + irGet(valueParameters[0]) + ) + val argument = valueParameters[0] + val thisValue = + irTemporary(coerceInlineClasses(irGet(argument), argument.type, constructorImpl.returnType, skipCast = true)) + valueMap[valueClass.thisReceiver!!.symbol] = thisValue + +irCall(constructorImpl.symbol).apply { + putValueArgument(0, irGet(valueParameters[0])) + } + } + } + } + valueClass.declarations.removeAll(initBlocks) - valueClass.declarations += function + valueClass.declarations += constructorImpl } private fun buildBoxFunction(valueClass: IrClass) { val function = context.inlineClassReplacements.getBoxFunction(valueClass) with(context.createIrBuilder(function.symbol)) { + val constructor = checkNotNull(valueClass.primaryConstructor) { + "Building box function in value class without a primary constructor" + } function.body = irExprBody( - irCall(valueClass.primaryConstructor!!.symbol).apply { + irCall(constructor.symbol).apply { passTypeArgumentsFrom(function) putValueArgument(0, irGet(function.valueParameters[0])) + if (valueClass.isExposedSingleFieldValueClass) + putValueArgument(1, irNull()) } ) } @@ -527,4 +689,33 @@ valueClass.declarations += function } + override fun handleRegularClassConstructor(constructor: IrConstructor) { + if (constructor.valueParameters.none { it.type.classOrNull?.owner?.isExposedSingleFieldValueClass == true }) return + if (constructor.origin == JvmLoweredDeclarationOrigin.FUNCTION_WITH_EXPOSED_INLINE_CLASS) return + if (DescriptorVisibilities.isPrivate(constructor.visibility)) return + val valueClass = constructor.parentAsClass + // Adding secondary constructor for Java with boxed exposed value class parameters + valueClass.addConstructor { + updateFrom(constructor) + isPrimary = false + origin = JvmLoweredDeclarationOrigin.FUNCTION_WITH_EXPOSED_INLINE_CLASS + returnType = constructor.returnType + }.apply { + copyParameterDeclarationsFrom(constructor) + + // Don't create a default argument stub for the constructor for Java + for (param in valueParameters) { + param.defaultValue = null + } + + copyAnnotationsFrom(constructor) + body = context.createIrBuilder(symbol).irBlockBody(this) { + +irDelegatingConstructorCall(constructor).also { + for ((idx, param) in this@apply.valueParameters.withIndex()) { + it.putValueArgument(idx, irGet(param)) + } + } + } + } + } }
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt index b2d4574..24317c0 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmMultiFieldValueClassLowering.kt
@@ -443,6 +443,26 @@ } } + override fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> { + replacement.valueParameters.forEach { + visitParameter(it) + it.defaultValue?.patchDeclarationParents(replacement) + } + + allScopes.push(createScope(replacement)) + replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement) + allScopes.pop() + replacement.copyAttributes(function) + + // Don't create a wrapper for functions which are only used in an unboxed context + if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null) + return listOf(replacement) + + val bridgeFunction = createBridgeFunction(function, replacement) + return listOf(replacement, bridgeFunction) + } + + override fun transformSecondaryConstructorFlat(constructor: IrConstructor, replacement: IrSimpleFunction): List<IrDeclaration> { for (param in replacement.valueParameters) { param.defaultValue?.patchDeclarationParents(replacement)
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt index 0bd5722..f652896 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmValueClassAbstractLowering.kt
@@ -40,6 +40,7 @@ if (replacement == null) { if (function is IrConstructor) { + handleRegularClassConstructor(function) val constructorReplacement = replacements.getReplacementForRegularClassConstructor(function) if (constructorReplacement != null) { addBindingsFor(function, constructorReplacement) @@ -77,6 +78,9 @@ } } + protected open fun handleRegularClassConstructor(constructor: IrConstructor) { + } + private fun transformFlattenedConstructor(function: IrConstructor, replacement: IrConstructor): List<IrDeclaration> { replacement.valueParameters.forEach { it.defaultValue?.patchDeclarationParents(replacement) @@ -105,24 +109,7 @@ return declaration } - private fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> { - replacement.valueParameters.forEach { - it.defaultValue?.patchDeclarationParents(replacement) - visitParameter(it) - } - allScopes.push(createScope(replacement)) - replacement.body = function.body?.transform(this, null)?.patchDeclarationParents(replacement) - allScopes.pop() - replacement.copyAttributes(function) - - // Don't create a wrapper for functions which are only used in an unboxed context - if (function.overriddenSymbols.isEmpty() || replacement.dispatchReceiverParameter != null) - return listOf(replacement) - - val bridgeFunction = createBridgeFunction(function, replacement) - - return listOf(replacement, bridgeFunction) - } + protected abstract fun transformSimpleFunctionFlat(function: IrSimpleFunction, replacement: IrSimpleFunction): List<IrDeclaration> final override fun visitReturn(expression: IrReturn): IrExpression { (expression.returnTargetSymbol.owner as? IrFunction)?.let { target -> @@ -177,7 +164,7 @@ protected enum class SpecificMangle { Inline, MultiField } protected abstract val specificMangle: SpecificMangle - private fun createBridgeFunction( + protected fun createBridgeFunction( function: IrSimpleFunction, replacement: IrSimpleFunction ): IrSimpleFunction {
diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt index d5ebb37..6baf9ee 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt
@@ -16,6 +16,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.hasChild import org.jetbrains.kotlin.backend.jvm.ir.isReadOfCrossinline +import org.jetbrains.kotlin.backend.jvm.mapping.mapType import org.jetbrains.kotlin.codegen.coroutines.COROUTINE_LABEL_FIELD_NAME import org.jetbrains.kotlin.codegen.coroutines.INVOKE_SUSPEND_METHOD_NAME import org.jetbrains.kotlin.codegen.coroutines.SUSPEND_FUNCTION_COMPLETION_PARAMETER_NAME @@ -177,7 +178,7 @@ val parametersFields = function.explicitParameters.map { val field = if (it in usedParams) addField { - val normalizedType = context.defaultTypeMapper.mapType(it.type).normalize() + val normalizedType = context.defaultTypeMapper.mapType(it).normalize() val index = varsCountByType[normalizedType]?.plus(1) ?: 0 varsCountByType[normalizedType] = index // Rename `$this` to avoid being caught by inlineCodegenUtils.isCapturedFieldName()
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt index 6ffbfe7..d586d66 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/InlineClassAbi.kt
@@ -7,6 +7,7 @@ import org.jetbrains.kotlin.backend.jvm.MemoizedMultiFieldValueClassReplacements.RemappedParameter.MultiFieldValueClassMapping import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound +import org.jetbrains.kotlin.backend.jvm.ir.isExposedSingleFieldValueClass import org.jetbrains.kotlin.backend.jvm.ir.isInlineClassType import org.jetbrains.kotlin.backend.jvm.ir.isValueClassType import org.jetbrains.kotlin.builtins.StandardNames @@ -20,6 +21,7 @@ import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin /** * Replace inline classes by their underlying types. @@ -65,7 +67,7 @@ assert(irFunction.constructedClass.isValue) { "Should not mangle names of non-inline class constructors: ${irFunction.render()}" } - return Name.identifier("constructor-impl") + return Name.identifier("constructor${JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS}") } if (irFunction.isAlreadyMangledMfvcFunction(context)) { return irFunction.name @@ -87,7 +89,7 @@ irFunction.name.asString() } - return Name.identifier("$base-${suffix ?: "impl"}") + return Name.identifier("$base${if (suffix == null) JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS else "-$suffix"}") } private fun IrFunction.isAlreadyMangledMfvcFunction(context: JvmBackendContext) = @@ -144,16 +146,21 @@ val IrFunction.fullValueParameterList: List<IrValueParameter> get() = listOfNotNull(extensionReceiverParameter) + valueParameters -fun IrFunction.hasMangledParameters(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean = - (dispatchReceiverParameter != null && when { - parentAsClass.isSingleFieldValueClass -> includeInline +fun IrFunction.hasMangledParameters(includeInline: Boolean = true, includeMFVC: Boolean = true): Boolean { + val includesInlineAndNotExposed = includeInline && origin != JvmLoweredDeclarationOrigin.FUNCTION_WITH_EXPOSED_INLINE_CLASS + + return (dispatchReceiverParameter != null && when { + parentAsClass.isSingleFieldValueClass -> includesInlineAndNotExposed parentAsClass.isMultiFieldValueClass -> includeMFVC else -> false - }) || fullValueParameterList.any { it.type.getRequiresMangling(includeInline, includeMFVC) } || (this is IrConstructor && when { - constructedClass.isSingleFieldValueClass -> includeInline + }) || fullValueParameterList.any { + it.type.getRequiresMangling(includesInlineAndNotExposed, includeMFVC) + } || (this is IrConstructor && when { + constructedClass.isSingleFieldValueClass -> includesInlineAndNotExposed constructedClass.isMultiFieldValueClass -> includeMFVC else -> false }) +} val IrFunction.hasMangledReturnType: Boolean get() = returnType.isInlineClassType() && parentClassOrNull?.isFileClass != true
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweredDeclarationOrigin.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweredDeclarationOrigin.kt index c4529ac..a4d983a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweredDeclarationOrigin.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLoweredDeclarationOrigin.kt
@@ -29,9 +29,10 @@ object ENUM_MAPPINGS_FOR_WHEN : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_WHEN", isSynthetic = true) object ENUM_MAPPINGS_FOR_ENTRIES : IrDeclarationOriginImpl("ENUM_MAPPINGS_FOR_ENTRIES", isSynthetic = true) object SYNTHETIC_INLINE_CLASS_MEMBER : IrDeclarationOriginImpl("SYNTHETIC_INLINE_CLASS_MEMBER", isSynthetic = true) - object SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER : + object SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER : IrDeclarationOriginImpl("SYNTHETIC_MULTI_FIELD_VALUE_CLASS_MEMBER", isSynthetic = true) object INLINE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("INLINE_CLASS_GENERATED_IMPL_METHOD") + object FUNCTION_WITH_EXPOSED_INLINE_CLASS : IrDeclarationOriginImpl("FUNCTION_WITH_EXPOSED_INLINE_CLASS") object MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD : IrDeclarationOriginImpl("MULTI_FIELD_VALUE_CLASS_GENERATED_IMPL_METHOD") object STATIC_INLINE_CLASS_REPLACEMENT : IrDeclarationOriginImpl("STATIC_INLINE_CLASS_REPLACEMENT") object STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT : IrDeclarationOriginImpl("STATIC_MULTI_FIELD_VALUE_CLASS_REPLACEMENT")
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt index feb56eb..d6fcbec 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/MemoizedInlineClassReplacements.kt
@@ -135,7 +135,7 @@ private val specializedEqualsCache = storageManager.createCacheWithNotNullValues<IrClass, IrSimpleFunction>() fun getSpecializedEqualsMethod(irClass: IrClass, irBuiltIns: IrBuiltIns): IrSimpleFunction { - require(irClass.isSingleFieldValueClass) + require(irClass.isSingleFieldValueClass) { "Not a single-field inline class" } return specializedEqualsCache.computeIfAbsent(irClass) { irFactory.buildFun { name = InlineClassDescriptorResolver.SPECIALIZED_EQUALS_NAME
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt index dd8595e..a4913aa 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmIrUtils.kt
@@ -54,6 +54,7 @@ import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.JvmNames import org.jetbrains.kotlin.name.JvmNames.JVM_DEFAULT_FQ_NAME import org.jetbrains.kotlin.name.JvmNames.JVM_DEFAULT_NO_COMPATIBILITY_FQ_NAME import org.jetbrains.kotlin.name.JvmNames.JVM_DEFAULT_WITH_COMPATIBILITY_FQ_NAME @@ -68,6 +69,9 @@ import org.jetbrains.org.objectweb.asm.commons.Method import java.io.File +val IrClass.isExposedSingleFieldValueClass: Boolean + get() = isSingleFieldValueClass && hasAnnotation(JvmNames.JVM_EXPOSE_BOXED) + fun IrDeclaration.getJvmNameFromAnnotation(): String? { // TODO lower @JvmName? val const = getAnnotation(DescriptorUtils.JVM_NAME)?.getValueArgument(0) as? IrConst<*> ?: return null
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/IrTypeMapping.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/IrTypeMapping.kt index 4dd3258..b19fffe 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/IrTypeMapping.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/IrTypeMapping.kt
@@ -5,13 +5,11 @@ package org.jetbrains.kotlin.backend.jvm.mapping +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter import org.jetbrains.kotlin.descriptors.DescriptorVisibilities -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrField -import org.jetbrains.kotlin.ir.declarations.IrValueParameter -import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.types.IrSimpleType import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.IrTypeArgument @@ -27,8 +25,14 @@ fun IrTypeMapper.mapType(irVariable: IrVariable): Type = mapType(irVariable.type) -fun IrTypeMapper.mapType(irValueParameter: IrValueParameter): Type = - mapType(irValueParameter.type) +fun IrTypeMapper.mapType(irValueParameter: IrValueParameter): Type { + val mode = if ((irValueParameter.parent as? IrFunction)?.origin == JvmLoweredDeclarationOrigin.FUNCTION_WITH_EXPOSED_INLINE_CLASS) + TypeMappingMode.DEFAULT.wrapInlineClassesMode() + else + TypeMappingMode.DEFAULT + + return mapType(irValueParameter.type, mode) +} fun IrTypeMapper.mapType(irField: IrField): Type = mapType(irField.type)
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/MethodSignatureMapper.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/MethodSignatureMapper.kt index d641362..114206d 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/MethodSignatureMapper.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/mapping/MethodSignatureMapper.kt
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil +import org.jetbrains.kotlin.name.JvmNames import org.jetbrains.kotlin.name.NameUtils import org.jetbrains.kotlin.resolve.jvm.JAVA_LANG_RECORD_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature @@ -195,6 +196,7 @@ // See also: KotlinTypeMapper.forceBoxedReturnType private fun forceBoxedReturnType(function: IrFunction): Boolean = isBoxMethodForInlineClass(function) || + function.origin == JvmLoweredDeclarationOrigin.FUNCTION_WITH_EXPOSED_INLINE_CLASS || forceFoxedReturnTypeOnOverride(function) || forceBoxedReturnTypeOnDefaultImplFun(function) || function.isFromJava() && function.returnType.isInlineClassType() @@ -345,24 +347,31 @@ } private fun writeParameterType(sw: JvmSignatureWriter, type: IrType, declaration: IrDeclaration, materialized: Boolean = true) { - if (sw.skipGenericSignature()) { + var mode = if (sw.skipGenericSignature()) { if (type.isInlineClassType() && declaration.isFromJava()) { - typeMapper.mapType(type, TypeMappingMode.GENERIC_ARGUMENT, sw, materialized) + TypeMappingMode.GENERIC_ARGUMENT } else { - typeMapper.mapType(type, TypeMappingMode.DEFAULT, sw, materialized) + TypeMappingMode.DEFAULT } - return + } else { + with(typeSystem) { + val extractTypeMappingModeFromAnnotation = extractTypeMappingModeFromAnnotation( + declaration.suppressWildcardsMode(), type, isForAnnotationParameter = false, mapTypeAliases = false + ) + when { + extractTypeMappingModeFromAnnotation != null -> extractTypeMappingModeFromAnnotation + declaration.isMethodWithDeclarationSiteWildcards && !declaration.isStaticInlineClassReplacement && type.argumentsCount() != 0 -> + TypeMappingMode.GENERIC_ARGUMENT // Render all wildcards + else -> typeSystem.getOptimalModeForValueParameter(type) + } + } } - val mode = with(typeSystem) { - extractTypeMappingModeFromAnnotation( - declaration.suppressWildcardsMode(), type, isForAnnotationParameter = false, mapTypeAliases = false - ) - ?: if (declaration.isMethodWithDeclarationSiteWildcards && !declaration.isStaticInlineClassReplacement && type.argumentsCount() != 0) { - TypeMappingMode.GENERIC_ARGUMENT // Render all wildcards - } else { - typeSystem.getOptimalModeForValueParameter(type) - } + + if (type.classOrNull?.owner?.isExposedSingleFieldValueClass == true && + declaration.origin == JvmLoweredDeclarationOrigin.FUNCTION_WITH_EXPOSED_INLINE_CLASS + ) { + mode = mode.wrapInlineClassesMode() } typeMapper.mapType(type, mode, sw, materialized)
diff --git a/compiler/testData/codegen/box/valueClasses/exposedInlineClass.kt b/compiler/testData/codegen/box/valueClasses/exposedInlineClass.kt new file mode 100644 index 0000000..5607a65 --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/exposedInlineClass.kt
@@ -0,0 +1,98 @@ +// WITH_STDLIB +// !LANGUAGE: +ContextReceivers +// TARGET_BACKEND: JVM_IR +// CHECK_BYTECODE_LISTING +// FILE: Example.kt +@file:OptIn(ExperimentalStdlibApi::class) + +package example + +import interop.* + +@Target(AnnotationTarget.CONSTRUCTOR) +annotation class A + +interface I { + fun virtualFunction(another: StringWrapper): StringWrapper +} + +@JvmInline +@JvmExposeBoxed +value class StringWrapper(val s: String) : I { + @A + constructor(i: Int) : this(i.toString()) + + init { + require(s != "") + } + + fun plainFunction(): Unit = println(s) + override fun virtualFunction(another: StringWrapper): StringWrapper = StringWrapper(s + another.s) +} + +fun topLevelFunction(e: StringWrapper): StringWrapper = e.virtualFunction(e) + +@JvmInline +@JvmExposeBoxed +value class IntWrapper(val i: Int) : I { + constructor(l: Long) : this(l.toInt()) + + init { + require(i != 0) + } + + fun plainFunction(another: IntWrapper): IntWrapper = IntWrapper(i + another.i) + + fun IntWrapper.extensionFunction(another: IntWrapper): IntWrapper = IntWrapper(this.i + this@IntWrapper.i + another.i) + + fun StringWrapper.extensionFunction(another: IntWrapper): IntWrapper = IntWrapper(another.i) + + override fun virtualFunction(another: StringWrapper): StringWrapper = StringWrapper(i.toString() + another.s) +} + +fun topLevelFunction(e: IntWrapper): IntWrapper = e.plainFunction(e) + +fun IntWrapper.topLevelFunction(e: IntWrapper): IntWrapper = when { + e.i > 100 -> IntWrapper(42) + else -> e.plainFunction(e) +} + +fun assertEquals(value1: Any?, value2: Any?) { + if (value1 != value2) + throw AssertionError("Expected $value1, got $value2") +} + +data class HasWrappers(val i: IntWrapper = IntWrapper(42), val j: StringWrapper = StringWrapper(84)) + +fun box(): String { + val w = IntWrapper(42L) + assertEquals(IntWrapper(84), w.plainFunction(w)) + val x = StringWrapper(42) + assertEquals(StringWrapper("4242"), x.virtualFunction(x)) + assertEquals(StringWrapper("4242"), w.virtualFunction(x)) + assertEquals(IntWrapper(84), topLevelFunction(w)) + assertEquals(StringWrapper("4242"), topLevelFunction(x)) + val h = HasWrappers(w) + assertEquals(w, h.i) + Interop.main() + return "OK" +} + +// FILE: interop/Interop.java +package interop; + +import example.*; + +public class Interop { + public static void main() { + IntWrapper wrp = new IntWrapper(42L); + ExampleKt.assertEquals(new IntWrapper(84), wrp.plainFunction(wrp)); + StringWrapper x = new StringWrapper(42); + ExampleKt.assertEquals(new StringWrapper("4242"), x.virtualFunction(x)); + ExampleKt.assertEquals(new StringWrapper("4242"), wrp.virtualFunction(x)); + ExampleKt.assertEquals(new IntWrapper(84L), ExampleKt.topLevelFunction(wrp)); + ExampleKt.assertEquals(new StringWrapper("4242"), ExampleKt.topLevelFunction(x)); + HasWrappers h = new HasWrappers(wrp, x); + ExampleKt.assertEquals(wrp, h.getI()); + } +}
diff --git a/compiler/testData/codegen/box/valueClasses/exposedInlineClass.txt b/compiler/testData/codegen/box/valueClasses/exposedInlineClass.txt new file mode 100644 index 0000000..190ca2c --- /dev/null +++ b/compiler/testData/codegen/box/valueClasses/exposedInlineClass.txt
@@ -0,0 +1,112 @@ +@kotlin.annotation.Target(allowedTargets=[CONSTRUCTOR]) +@java.lang.annotation.Retention(value=RUNTIME) +@java.lang.annotation.Target(value=[CONSTRUCTOR]) +@kotlin.Metadata +public annotation class example/A { + // source: 'Example.kt' +} + +@kotlin.Metadata +public final class example/ExampleKt { + // source: 'Example.kt' + public final static method assertEquals(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Object): void + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method topLevelFunction(@org.jetbrains.annotations.NotNull p0: example.IntWrapper): example.IntWrapper + public final static @org.jetbrains.annotations.NotNull method topLevelFunction(@org.jetbrains.annotations.NotNull p0: example.IntWrapper, @org.jetbrains.annotations.NotNull p1: example.IntWrapper): example.IntWrapper + public final static @org.jetbrains.annotations.NotNull method topLevelFunction(@org.jetbrains.annotations.NotNull p0: example.StringWrapper): example.StringWrapper + public final static method topLevelFunction-JIAth_k(p0: int, p1: int): int + public final static method topLevelFunction-OeTM1uU(p0: int): int + public final static @org.jetbrains.annotations.NotNull method topLevelFunction-bzFBYxs(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String +} + +@kotlin.Metadata +public final class example/HasWrappers { + // source: 'Example.kt' + private final field i: int + private final @org.jetbrains.annotations.NotNull field j: java.lang.String + public method <init>(@org.jetbrains.annotations.NotNull p0: example.IntWrapper, @org.jetbrains.annotations.NotNull p1: example.StringWrapper): void + private method <init>(p0: int, p1: java.lang.String): void + public synthetic method <init>(p0: int, p1: java.lang.String, p2: int, p3: kotlin.jvm.internal.DefaultConstructorMarker): void + public synthetic method <init>(p0: int, p1: java.lang.String, p2: kotlin.jvm.internal.DefaultConstructorMarker): void + public final @org.jetbrains.annotations.NotNull method component1(): example.IntWrapper + public final method component1-P7HTv5Y(): int + public final @org.jetbrains.annotations.NotNull method component2(): example.StringWrapper + public final @org.jetbrains.annotations.NotNull method component2-yiaqiYc(): java.lang.String + public final @org.jetbrains.annotations.NotNull method copy(@org.jetbrains.annotations.NotNull p0: example.IntWrapper, @org.jetbrains.annotations.NotNull p1: example.StringWrapper): example.HasWrappers + public synthetic static method copy-026PlWA$default(p0: example.HasWrappers, p1: int, p2: java.lang.String, p3: int, p4: java.lang.Object): example.HasWrappers + public final @org.jetbrains.annotations.NotNull method copy-026PlWA(p0: int, @org.jetbrains.annotations.NotNull p1: java.lang.String): example.HasWrappers + public method equals(@org.jetbrains.annotations.Nullable p0: java.lang.Object): boolean + public final @org.jetbrains.annotations.NotNull method getI(): example.IntWrapper + public final method getI-P7HTv5Y(): int + public final @org.jetbrains.annotations.NotNull method getJ(): example.StringWrapper + public final @org.jetbrains.annotations.NotNull method getJ-yiaqiYc(): java.lang.String + public method hashCode(): int + public @org.jetbrains.annotations.NotNull method toString(): java.lang.String +} + +@kotlin.Metadata +public interface example/I { + // source: 'Example.kt' + public abstract @org.jetbrains.annotations.NotNull method virtualFunction(@org.jetbrains.annotations.NotNull p0: example.StringWrapper): example.StringWrapper + public abstract @org.jetbrains.annotations.NotNull method virtualFunction-cnKg8a0(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +@kotlin.jvm.JvmExposeBoxed +public final class example/IntWrapper { + // source: 'Example.kt' + private final field i: int + public method <init>(p0: int): void + private synthetic method <init>(p0: int, p1: java.lang.Void): void + public method <init>(p0: long): void + public synthetic final static method box-impl(p0: int): example.IntWrapper + public static method constructor-impl(p0: int): int + public static method constructor-impl(p0: long): int + public method equals(p0: java.lang.Object): boolean + public static method equals-impl(p0: int, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: int, p1: int): boolean + public final @org.jetbrains.annotations.NotNull method extensionFunction(@org.jetbrains.annotations.NotNull p0: example.IntWrapper, @org.jetbrains.annotations.NotNull p1: example.IntWrapper): example.IntWrapper + public final @org.jetbrains.annotations.NotNull method extensionFunction(@org.jetbrains.annotations.NotNull p0: example.StringWrapper, @org.jetbrains.annotations.NotNull p1: example.IntWrapper): example.IntWrapper + public final static method extensionFunction-7sm2r4Y(p0: int, @org.jetbrains.annotations.NotNull p1: java.lang.String, p2: int): int + public final static method extensionFunction-ke8FMIE(p0: int, p1: int, p2: int): int + public final method getI(): int + public method hashCode(): int + public static method hashCode-impl(p0: int): int + public final @org.jetbrains.annotations.NotNull method plainFunction(@org.jetbrains.annotations.NotNull p0: example.IntWrapper): example.IntWrapper + public final static method plainFunction-IkJdR0s(p0: int, p1: int): int + public method toString(): java.lang.String + public static method toString-impl(p0: int): java.lang.String + public synthetic final method unbox-impl(): int + public @org.jetbrains.annotations.NotNull method virtualFunction(@org.jetbrains.annotations.NotNull p0: example.StringWrapper): example.StringWrapper + public @org.jetbrains.annotations.NotNull method virtualFunction-cnKg8a0(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String + public static @org.jetbrains.annotations.NotNull method virtualFunction-cnKg8a0(p0: int, @org.jetbrains.annotations.NotNull p1: java.lang.String): java.lang.String +} + +@kotlin.jvm.JvmInline +@kotlin.Metadata +@kotlin.jvm.JvmExposeBoxed +public final class example/StringWrapper { + // source: 'Example.kt' + private final @org.jetbrains.annotations.NotNull field s: java.lang.String + public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public @example.A method <init>(p0: int): void + private synthetic method <init>(p0: java.lang.String, p1: java.lang.Void): void + public synthetic final static method box-impl(p0: java.lang.String): example.StringWrapper + public static @org.jetbrains.annotations.NotNull method constructor-impl(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String + public static @example.A @org.jetbrains.annotations.NotNull method constructor-impl(p0: int): java.lang.String + public method equals(p0: java.lang.Object): boolean + public static method equals-impl(p0: java.lang.String, p1: java.lang.Object): boolean + public final static method equals-impl0(p0: java.lang.String, p1: java.lang.String): boolean + public final @org.jetbrains.annotations.NotNull method getS(): java.lang.String + public method hashCode(): int + public static method hashCode-impl(p0: java.lang.String): int + public final method plainFunction(): void + public final static method plainFunction-impl(p0: java.lang.String): void + public method toString(): java.lang.String + public static method toString-impl(p0: java.lang.String): java.lang.String + public synthetic final method unbox-impl(): java.lang.String + public @org.jetbrains.annotations.NotNull method virtualFunction(@org.jetbrains.annotations.NotNull p0: example.StringWrapper): example.StringWrapper + public @org.jetbrains.annotations.NotNull method virtualFunction-cnKg8a0(@org.jetbrains.annotations.NotNull p0: java.lang.String): java.lang.String + public static @org.jetbrains.annotations.NotNull method virtualFunction-cnKg8a0(p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: java.lang.String): java.lang.String +}
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 51fa747..5086442 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java
@@ -52440,6 +52440,12 @@ } @Test + @TestMetadata("exposedInlineClass.kt") + public void testExposedInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/exposedInlineClass.kt"); + } + + @Test @TestMetadata("fakeOverrideCall.kt") public void testFakeOverrideCall() throws Exception { runTest("compiler/testData/codegen/box/valueClasses/fakeOverrideCall.kt");
diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java index 94cf84b..5bc6194 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenWithIrInlinerTestGenerated.java
@@ -52440,6 +52440,12 @@ } @Test + @TestMetadata("exposedInlineClass.kt") + public void testExposedInlineClass() throws Exception { + runTest("compiler/testData/codegen/box/valueClasses/exposedInlineClass.kt"); + } + + @Test @TestMetadata("fakeOverrideCall.kt") public void testFakeOverrideCall() throws Exception { runTest("compiler/testData/codegen/box/valueClasses/fakeOverrideCall.kt");
diff --git a/core/compiler.common.jvm/src/org/jetbrains/kotlin/name/JvmNames.kt b/core/compiler.common.jvm/src/org/jetbrains/kotlin/name/JvmNames.kt index f9309b1..1e22d5a 100644 --- a/core/compiler.common.jvm/src/org/jetbrains/kotlin/name/JvmNames.kt +++ b/core/compiler.common.jvm/src/org/jetbrains/kotlin/name/JvmNames.kt
@@ -14,6 +14,15 @@ val JVM_NAME_SHORT: String = JVM_NAME.shortName().asString() + @JvmField + val JVM_EXPOSE_BOXED: FqName = FqName("kotlin.jvm.JvmExposeBoxed") + + @JvmField + val JVM_EXPOSE_BOXED_CLASS_ID = ClassId.topLevel(JVM_EXPOSE_BOXED) + + @JvmField + val JVM_EXPOSE_BOXED_SHORT: String = JVM_EXPOSE_BOXED.shortName().asString() + val JVM_MULTIFILE_CLASS: FqName = FqName("kotlin.jvm.JvmMultifileClass") val JVM_MULTIFILE_CLASS_ID: ClassId = ClassId(FqName("kotlin.jvm"), Name.identifier("JvmMultifileClass")) val JVM_MULTIFILE_CLASS_SHORT = JVM_MULTIFILE_CLASS.shortName().asString()
diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt index ef0baa2..062d2ed 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/inlineClassesUtils.kt
@@ -19,6 +19,9 @@ val JVM_INLINE_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmInline") val JVM_INLINE_ANNOTATION_CLASS_ID = ClassId.topLevel(JVM_INLINE_ANNOTATION_FQ_NAME) +val JVM_EXPOSE_BOXED_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmExposeBoxed") +val JVM_EXPOSE_BOXED_ANNOTATION_CLASS_ID = ClassId.topLevel(JVM_EXPOSE_BOXED_ANNOTATION_FQ_NAME) + // FIXME: DeserializedClassDescriptor in reflection do not have @JvmInline annotation, that we // FIXME: would like to check as well. fun DeclarationDescriptor.isInlineClass(): Boolean = this is ClassDescriptor && this.valueClassRepresentation is InlineClassRepresentation
diff --git a/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt b/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt index bd072e6..b2a86da 100644 --- a/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt +++ b/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt
@@ -192,3 +192,24 @@ @SinceKotlin("1.8") @OptionalExpectation public expect annotation class JvmSerializableLambda() + +/** + * Instructs the compiler + * to expose the API related to the annotated [kotlin.jvm.JvmInline] value classes as its boxed variant for effective usage from Java. + * + * Particularly, it performs the following transformations: + * + * - For functions and constructors that take or return inline classes, + * a wrapper declaration for Java is created where inline classes are boxed. + * - Functions declared in the marked class can be called normally from Java. + * - Constructor available from Java is added. + * + * These additions preserve backwards compatibility (both binary and source), so existing inline classes can be marked safely. + */ +@Target(CLASS) +@Retention(AnnotationRetention.BINARY) +@MustBeDocumented +@SinceKotlin("1.9") +@OptionalExpectation +@ExperimentalStdlibApi +public expect annotation class JvmExposeBoxed()
diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmPlatformAnnotations.kt b/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmPlatformAnnotations.kt index 591c200..0c223e4 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmPlatformAnnotations.kt +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmPlatformAnnotations.kt
@@ -154,3 +154,23 @@ @MustBeDocumented @SinceKotlin("1.5") public actual annotation class JvmRecord + +/** + * Instructs the compiler + * to expose the API related to the annotated [kotlin.jvm.JvmInline] value classes as its boxed variant for effective usage from Java. + * + * Particularly, it performs the following transformations: + * + * - For functions and constructors that take or return inline classes, + * a wrapper declaration for Java is created where inline classes are boxed. + * - Functions declared in the marked class can be called normally from Java. + * - Constructor available from Java is added. + * + * These additions preserve backwards compatibility (both binary and source), so existing inline classes can be marked safely. + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +@MustBeDocumented +@SinceKotlin("1.9") +@ExperimentalStdlibApi +public actual annotation class JvmExposeBoxed
diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index b0e9ca7..21d96ee 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt
@@ -3403,6 +3403,9 @@ public abstract interface annotation class kotlin/jvm/JvmDefaultWithoutCompatibility : java/lang/annotation/Annotation { } +public abstract interface annotation class kotlin/jvm/JvmExposeBoxed : java/lang/annotation/Annotation { +} + public abstract interface annotation class kotlin/jvm/JvmField : java/lang/annotation/Annotation { }