diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 04cb8163ea8..4c79c6a9a6e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -258,13 +258,16 @@ class ClassCodegen private constructor( extraFlags = extraFlags or JvmAnnotationNames.METADATA_SCRIPT_FLAG } - // There are three kinds of classes which are regenerated during inlining. + // There are four kinds of classes which are regenerated during inlining. // 1) Anonymous classes which are in the scope of an inline function. // 2) SAM wrappers used in an inline function. These are identified by name, since they // can be reused in different functions and are thus generated in the enclosing top-level // class instead of inside of an inline function. - // 3) WhenMapping classes used from inline functions. These are collected in + // 3) WhenMapping classes used from public inline functions. These are collected in // `JvmBackendContext.publicAbiSymbols` in `MappedEnumWhenLowering`. + // 4) Annotation implementation classes used from public inline function. Similar to + // public WhenMapping classes, these are collected in `publicAbiSymbols` in + // `JvmAnnotationImplementationTransformer`. val isPublicAbi = irClass.symbol in context.publicAbiSymbols || irClass.isInlineSamWrapper || type.isAnonymousClass && irClass.isInPublicInlineScope diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt index c163521b593..a73db7628fd 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmAnnotationImplementationTransformer.kt @@ -11,18 +11,20 @@ import org.jetbrains.kotlin.backend.common.lower.AnnotationImplementationTransfo import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder +import org.jetbrains.kotlin.backend.jvm.ir.isInPublicInlineScope import org.jetbrains.kotlin.backend.jvm.lower.FunctionReferenceLowering.Companion.javaClassReference import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.expressions.IrConstructorCall import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.findDeclaration -import org.jetbrains.kotlin.ir.util.isPrimitiveArray -import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.utils.addToStdlib.safeAs internal val annotationImplementationPhase = makeIrFilePhase( { ctxt -> AnnotationImplementationLowering { JvmAnnotationImplementationTransformer(ctxt, it) } }, @@ -32,6 +34,20 @@ internal val annotationImplementationPhase = makeIrFilePhase( class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, file: IrFile) : AnnotationImplementationTransformer(jvmContext, file) { + private val publicAnnotationImplementationClasses = mutableSetOf() + + // FIXME: Copied from JvmSingleAbstractMethodLowering + private val inInlineFunctionScope: Boolean + get() = allScopes.any { it.irElement.safeAs()?.isInPublicInlineScope == true } + + override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { + val constructedClass = expression.type.classOrNull + if (constructedClass?.owner?.isAnnotationClass == true && inInlineFunctionScope) { + publicAnnotationImplementationClasses += constructedClass + } + return super.visitConstructorCall(expression) + } + override fun IrType.kClassToJClassIfNeeded(): IrType = when { this.isKClass() -> jvmContext.ir.symbols.javaLangClass.starProjectedType this.isKClassArray() -> jvmContext.irBuiltIns.arrayClass.typeWith( @@ -72,6 +88,12 @@ class JvmAnnotationImplementationTransformer(val jvmContext: JvmBackendContext, } override fun implementPlatformSpecificParts(annotationClass: IrClass, implClass: IrClass) { + // Mark the implClass as part of the public ABI if it was instantiated from a public + // inline function, since annotation implementation classes are regenerated during inlining. + if (annotationClass.symbol in publicAnnotationImplementationClasses) { + jvmContext.publicAbiSymbols += implClass.symbol + } + implClass.addFunction( name = "annotationType", returnType = jvmContext.ir.symbols.javaLangClass.starProjectedType,