diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index b46b0c0f30b..7fedf311434 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -27008,6 +27008,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT public void testReflectOnLambdaInSuspendLambda() throws Exception { runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt"); } + + @TestMetadata("reflectOnSuspendLambdaInField.kt") + public void testReflectOnSuspendLambdaInField() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt"); + } } @TestMetadata("compiler/testData/codegen/box/reflection/mapping") diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 8869ef67103..6d05ba86911 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -102,6 +102,12 @@ internal val propertiesPhase = makeIrFilePhase( stickyPostconditions = setOf((PropertiesLowering)::checkNoProperties) ) +internal val IrClass.isGeneratedLambdaClass: Boolean + get() = origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || + origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA || + origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL || + origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE + internal val localDeclarationsPhase = makeIrFilePhase( { context -> LocalDeclarationsLowering( @@ -111,12 +117,10 @@ internal val localDeclarationsPhase = makeIrFilePhase( NameUtils.sanitizeAsJavaIdentifier(super.localName(declaration)) }, object : VisibilityPolicy { + // Note: any condition that results in non-`LOCAL` visibility here should be duplicated in `JvmLocalClassPopupLowering`, + // else it won't detect the class as local. override fun forClass(declaration: IrClass, inInlineFunctionScope: Boolean): DescriptorVisibility = - if (declaration.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || - declaration.origin == JvmLoweredDeclarationOrigin.SUSPEND_LAMBDA || - declaration.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL || - declaration.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE - ) { + if (declaration.isGeneratedLambdaClass) { scopedVisibility(inInlineFunctionScope) } else { declaration.visibility diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmLocalClassPopupLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmLocalClassPopupLowering.kt index 7e0150a300f..84cf5b00c80 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmLocalClassPopupLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/JvmLocalClassPopupLowering.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.lower.LocalClassPopupLowering import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.IrInlineReferenceLocator +import org.jetbrains.kotlin.backend.jvm.isGeneratedLambdaClass import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.IrFunctionReference @@ -31,15 +32,11 @@ class JvmLocalClassPopupLowering(context: JvmBackendContext) : LocalClassPopupLo // On JVM, we only pop up local classes in field initializers and anonymous init blocks, so that InitializersLowering would not copy // them to each constructor. (Moving all local classes is not possible because of cases where they use reified type parameters, // or capture crossinline lambdas.) - // Upon moving such class, we record customEnclosingFunction for it to be the class constructor. This is needed because otherwise - // the class will not get any EnclosingMethod in the codegen later, since it won't be local anymore. + // Upon moving such class, we record that it used to be in an initializer so that the codegen later sets its EnclosingMethod + // to the primary constructor. override fun shouldPopUp(klass: IrClass, currentScope: ScopeWithIr?): Boolean { - // On JVM, lambdas have package-private visibility after LocalDeclarationsLowering, so we have to check something else. - val isLocal = super.shouldPopUp(klass, currentScope) || - klass.origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || - klass.origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL || - klass.origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE - if (!isLocal) return false + // On JVM, lambdas have package-private visibility after LocalDeclarationsLowering; see `forClass` in `localDeclarationsPhase`. + if (!super.shouldPopUp(klass, currentScope) && !klass.isGeneratedLambdaClass) return false var parent = currentScope?.irElement while (parent is IrFunction) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt index 9d60f5653dd..429dce75b1a 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/SuspendLambdaLowering.kt @@ -187,6 +187,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin addInvokeCallingConstructor(constructor, invokeSuspend, invokeToOverride, parametersFields) } + this.metadata = function.metadata context.suspendLambdaToOriginalFunctionMap[attributeOwnerId as IrFunctionReference] = function } diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt new file mode 100644 index 00000000000..e0acf8cdfac --- /dev/null +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt @@ -0,0 +1,12 @@ +// TARGET_BACKEND: JVM +// WITH_REFLECT + +import kotlin.reflect.jvm.reflect + +class C { + val x: suspend (String) -> Unit = { OK: String -> } +} + +fun box(): String { + return C().x.reflect()?.parameters?.singleOrNull()?.name ?: "null" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index d750ea5b7b0..8411de7e5dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -28774,6 +28774,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { public void testReflectOnLambdaInSuspendLambda() throws Exception { runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt"); } + + @TestMetadata("reflectOnSuspendLambdaInField.kt") + public void testReflectOnSuspendLambdaInField() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt"); + } } @TestMetadata("compiler/testData/codegen/box/reflection/mapping") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 36c8b39a798..89bfaaba44b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -26408,6 +26408,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes public void testReflectOnLambdaInSuspendLambda() throws Exception { runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt"); } + + @TestMetadata("reflectOnSuspendLambdaInField.kt") + public void testReflectOnSuspendLambdaInField() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt"); + } } @TestMetadata("compiler/testData/codegen/box/reflection/mapping") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index b08d61b0a2e..c1260496a7f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -27008,6 +27008,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes public void testReflectOnLambdaInSuspendLambda() throws Exception { runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt"); } + + @TestMetadata("reflectOnSuspendLambdaInField.kt") + public void testReflectOnSuspendLambdaInField() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt"); + } } @TestMetadata("compiler/testData/codegen/box/reflection/mapping")