diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index a6798f079cd..7bd1a1cc4fa 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -1290,6 +1290,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/annotations/javaAnnotationWithClassArray.kt"); } + @Test + @TestMetadata("JvmSerializableLambdaAnnotation.kt") + public void testJvmSerializableLambdaAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.kt"); + } + @Test @TestMetadata("kt1860-negative.kt") public void testKt1860_negative() throws Exception { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index a695625077a..e2aa36113af 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -253,13 +253,12 @@ class GenerationState private constructor( JvmClosureGenerationScheme.CLASS } - val lambdasScheme = run { - val fromConfig = configuration.get(JVMConfigurationKeys.LAMBDAS) - ?: JvmClosureGenerationScheme.DEFAULT - if (target >= fromConfig.minJvmTarget) - fromConfig - else - JvmClosureGenerationScheme.DEFAULT + val lambdasScheme = configuration.get(JVMConfigurationKeys.LAMBDAS).let { fromConfig -> + if (fromConfig == null || target < fromConfig.minJvmTarget) { + if (languageVersionSettings.supportsFeature(LanguageFeature.LightweightLambdas) && target >= JvmClosureGenerationScheme.INDY.minJvmTarget) + JvmClosureGenerationScheme.INDY + else JvmClosureGenerationScheme.CLASS + } else fromConfig } val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module) diff --git a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmClosureGenerationScheme.kt b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmClosureGenerationScheme.kt index 2bc87eea62a..d8995c54fff 100644 --- a/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmClosureGenerationScheme.kt +++ b/compiler/config.jvm/src/org/jetbrains/kotlin/config/JvmClosureGenerationScheme.kt @@ -16,8 +16,6 @@ enum class JvmClosureGenerationScheme( ; companion object { - val DEFAULT = CLASS - @JvmStatic fun fromString(string: String?): JvmClosureGenerationScheme? { val lowerStr = string?.toLowerCaseAsciiOnly() ?: return null diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 8e24a4afccb..aed18274fa9 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -1290,6 +1290,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/annotations/javaAnnotationWithClassArray.kt"); } + @Test + @TestMetadata("JvmSerializableLambdaAnnotation.kt") + public void testJvmSerializableLambdaAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.kt"); + } + @Test @TestMetadata("kt1860-negative.kt") public void testKt1860_negative() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 6633d432b95..b61052cecb3 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -1290,6 +1290,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/annotations/javaAnnotationWithClassArray.kt"); } + @Test + @TestMetadata("JvmSerializableLambdaAnnotation.kt") + public void testJvmSerializableLambdaAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.kt"); + } + @Test @TestMetadata("kt1860-negative.kt") public void testKt1860_negative() throws Exception { diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt index 2b339eb2399..e97ebc281e7 100644 --- a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt @@ -33,5 +33,6 @@ object JvmExpressionCheckers : ExpressionCheckers() { get() = setOf( FirJavaAnnotationsChecker, FirJvmPackageNameAnnotationsChecker, + FirJvmSerializableLambdaChecker, ) } diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmSerializableLambdaChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmSerializableLambdaChecker.kt new file mode 100644 index 00000000000..e73c1614531 --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJvmSerializableLambdaChecker.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2022 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.expression + +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.expression.FirAnnotationChecker +import org.jetbrains.kotlin.fir.analysis.checkers.getActualTargetList +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.resolve.fqName +import org.jetbrains.kotlin.name.FqName + +object FirJvmSerializableLambdaChecker : FirAnnotationChecker() { + private val JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSerializableLambda") + + override fun check(expression: FirAnnotation, context: CheckerContext, reporter: DiagnosticReporter) { + if (expression.fqName(context.session) == JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME) { + val declaration = context.containingDeclarations.last() + if (declaration !is FirAnonymousFunction) { + val actualTargets = getActualTargetList(declaration) + val targetDescription = actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target" + reporter.reportOn( + expression.source, + FirErrors.WRONG_ANNOTATION_TARGET, + targetDescription, + context + ) + } + } + } +} \ No newline at end of file diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index fb2ce4c4b0c..ba666e257cb 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -26346,6 +26346,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/invokedynamic/lambdas/genericLambdaSignature.kt"); } + @Test + @TestMetadata("jvmSerializableLambda.kt") + public void testJvmSerializableLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/lambdas/jvmSerializableLambda.kt"); + } + @Test @TestMetadata("kt52875.kt") public void testKt52875() throws Exception { @@ -42140,6 +42146,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/lambdaClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("lightweightLambdas.kt") + public void testLightweightLambdas() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/lightweightLambdas.kt"); + } + @Test @TestMetadata("parameterNamesAndNullability.kt") public void testParameterNamesAndNullability() throws Exception { diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt index aab332d09e8..5c5cde61801 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt @@ -38,6 +38,9 @@ val VOLATILE_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Volatile") @JvmField val TRANSIENT_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Transient") +@JvmField +val JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSerializableLambda") + fun DeclarationDescriptor.findJvmOverloadsAnnotation(): AnnotationDescriptor? = annotations.findAnnotation(JVM_OVERLOADS_FQ_NAME) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmSerializableLambdaAnnotationChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmSerializableLambdaAnnotationChecker.kt new file mode 100644 index 00000000000..7c19315dbf4 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmSerializableLambdaAnnotationChecker.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2010-2022 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.resolve.jvm.checkers + +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtAnnotated +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.resolve.AdditionalAnnotationChecker +import org.jetbrains.kotlin.resolve.BindingContext.ANNOTATION +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME + +object JvmSerializableLambdaAnnotationChecker : AdditionalAnnotationChecker { + override fun checkEntries( + entries: List, + actualTargets: List, + trace: BindingTrace, + annotated: KtAnnotated?, + languageVersionSettings: LanguageVersionSettings + ) { + entries.find { trace.get(ANNOTATION, it)?.fqName == JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME }?.let { + if (!actualTargets.contains(KotlinTarget.LAMBDA_EXPRESSION) && !actualTargets.contains(KotlinTarget.ANONYMOUS_FUNCTION)) { + trace.report( + Errors.WRONG_ANNOTATION_TARGET.on(it, actualTargets.firstOrNull()?.description ?: "unidentified target") + ) + } + } + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt index 936846eb25f..daf505f1a82 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/platform/JvmPlatformConfigurator.kt @@ -119,6 +119,7 @@ object JvmPlatformConfigurator : PlatformConfiguratorBase( container.useInstance(FunctionWithBigAritySupport.LanguageVersionDependent) container.useInstance(GenericArrayClassLiteralSupport.Enabled) container.useInstance(JavaActualAnnotationArgumentExtractor()) + container.useInstance(JvmSerializableLambdaAnnotationChecker) } override fun configureModuleDependentCheckers(container: StorageComponentContainer) { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 5b612a20e26..fdfa59419bb 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.util.OperatorNameConventions internal val functionReferencePhase = makeIrFilePhase( @@ -76,6 +77,9 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) private val shouldGenerateIndyLambdas = context.state.lambdasScheme == JvmClosureGenerationScheme.INDY + private val shouldGenerateLightweightLambdas = + shouldGenerateIndyLambdas && context.state.languageVersionSettings.supportsFeature(LanguageFeature.LightweightLambdas) + private val isJavaSamConversionWithEqualsHashCode = context.state.languageVersionSettings.supportsFeature(LanguageFeature.JavaSamConversionEqualsHashCode) @@ -525,7 +529,10 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) private inner class FunctionReferenceBuilder(val irFunctionReference: IrFunctionReference, val samSuperType: IrType? = null) { private val isLambda = irFunctionReference.origin.isLambda - + private val isLightweightLambda = isLambda + && shouldGenerateLightweightLambdas + && !irFunctionReference.symbol.owner.hasAnnotation(JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME) + private val isHeavyweightLambda = isLambda && !isLightweightLambda private val callee = irFunctionReference.symbol.owner // Only function references can bind a receiver and even then we can only bind either an extension or a dispatch receiver. @@ -605,7 +612,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) private val superType = samSuperType ?: when { - isLambda -> context.ir.symbols.lambdaClass + isLightweightLambda -> context.ir.symbols.any + isHeavyweightLambda -> context.ir.symbols.lambdaClass isFunInterfaceConstructorReference -> context.ir.symbols.funInterfaceConstructorReferenceClass useOptimizedSuperClass -> when { isAdaptedReference -> context.ir.symbols.adaptedFunctionReference @@ -642,7 +650,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) } createImplicitParameterDeclarationWithWrappedDescriptor() copyAttributes(irFunctionReference) - if (isLambda) { + if (!isLightweightLambda) { metadata = irFunctionReference.symbol.owner.metadata } } @@ -773,7 +781,8 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) context.irBuiltIns.anyClass.owner.constructors.single() else -> { val expectedArity = - if (isLambda && !isAdaptedReference) 1 + if (isLightweightLambda && !isAdaptedReference) 0 + else if (isHeavyweightLambda && !isAdaptedReference) 1 else 1 + (if (boundReceiver != null) 1 else 0) + (if (useOptimizedSuperClass) 4 else 0) superType.getClass()!!.constructors.single { it.valueParameters.size == expectedArity @@ -803,7 +812,9 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) call.putValueArgument(0, funInterfaceJavaClassRef) } else { var index = 0 - call.putValueArgument(index++, irInt(argumentTypes.size + if (irFunctionReference.isSuspend) 1 else 0)) + if (!isLightweightLambda) { + call.putValueArgument(index++, irInt(argumentTypes.size + if (irFunctionReference.isSuspend) 1 else 0)) + } if (boundReceiver != null) { call.putValueArgument(index++, generateBoundReceiver()) } diff --git a/compiler/testData/codegen/box/functions/functionNtoString.kt b/compiler/testData/codegen/box/functions/functionNtoString.kt index 881d20ab52d..af71ec32ba7 100644 --- a/compiler/testData/codegen/box/functions/functionNtoString.kt +++ b/compiler/testData/codegen/box/functions/functionNtoString.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // IGNORE_BACKEND: WASM // WASM_MUTE_REASON: IGNORED_IN_JS // IGNORE_BACKEND: JS_IR diff --git a/compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt b/compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt index 87c88b20fe2..8105b7ed1b5 100644 --- a/compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt +++ b/compiler/testData/codegen/box/functions/functionNtoStringGeneric.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // IGNORE_BACKEND: WASM // WASM_MUTE_REASON: IGNORED_IN_JS // IGNORE_BACKEND: JS_IR diff --git a/compiler/testData/codegen/box/invokedynamic/lambdas/jvmSerializableLambda.kt b/compiler/testData/codegen/box/invokedynamic/lambdas/jvmSerializableLambda.kt new file mode 100644 index 00000000000..f459873f244 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/lambdas/jvmSerializableLambda.kt @@ -0,0 +1,13 @@ +// LANGUAGE: +LightweightLambdas +// TARGET_BACKEND: JVM_IR +// JVM_TARGET: 1.8 +// FULL_JDK +// WITH_STDLIB + +import kotlin.test.assertTrue + +fun box(): String { + assertTrue((@kotlin.jvm.JvmSerializableLambda {}) is java.io.Serializable) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/lightweightLambdas.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/lightweightLambdas.kt new file mode 100644 index 00000000000..d26e406c0a0 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/lightweightLambdas.kt @@ -0,0 +1,21 @@ +// LANGUAGE: +LightweightLambdas +// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas +// TARGET_BACKEND: JVM_IR +// JVM_TARGET: 1.8 +// WITH_REFLECT + +import kotlin.reflect.jvm.reflect +import kotlin.test.* +import kotlin.jvm.JvmSerializableLambda + +fun box(): String { + assertNull({}.reflect()) + assertNull((fun () {}).reflect()) + assertNull((fun Any.() {}).reflect()) + + assertNotNull((@JvmSerializableLambda {}).reflect()) + assertNotNull((@JvmSerializableLambda fun () {}).reflect()) + assertNotNull((@JvmSerializableLambda fun Any.() {}).reflect()) + + return "OK" +} diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/parameterNamesAndNullability.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/parameterNamesAndNullability.kt index f9079b6ca29..bd0e55179d0 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/parameterNamesAndNullability.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/parameterNamesAndNullability.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithInlineClassArgument.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithInlineClassArgument.kt index 07f2da7c99d..d92dcd9a553 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithInlineClassArgument.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnDefaultWithInlineClassArgument.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInArrayConstructor.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInArrayConstructor.kt index aad6766ca32..d90976ec6ee 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInArrayConstructor.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInArrayConstructor.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInConstructor.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInConstructor.kt index 304d5549308..63ce7c18d29 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInConstructor.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInConstructor.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInField.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInField.kt index a91a9692495..cd7ad0e7cf2 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInField.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInField.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInStaticField.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInStaticField.kt index 07346cc7f78..b6a5797fe72 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInStaticField.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInStaticField.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspend.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspend.kt index 55311f56e50..b3c2d4aff42 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspend.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspend.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt index 7f6bb407776..256be011f5c 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnLambdaInSuspendLambda.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt index 1fa7cee8b3d..1f9232d6b84 100644 --- a/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt +++ b/compiler/testData/codegen/box/reflection/lambdaClasses/reflectOnSuspendLambdaInField.kt @@ -1,3 +1,4 @@ +// LAMBDAS: CLASS // !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas // TARGET_BACKEND: JVM // WITH_REFLECT diff --git a/compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.kt b/compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.kt new file mode 100644 index 00000000000..2277db830db --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.kt @@ -0,0 +1,17 @@ +// LAMBDAS: INDY +// FIR_IDENTICAL +// WITH_STDLIB + +import kotlin.jvm.JvmSerializableLambda + +fun foo() = fun () {} + +val good1 = @JvmSerializableLambda {} +val good2 = @JvmSerializableLambda fun () {} +val good3 = @JvmSerializableLambda fun Any.() {} +val good4 = listOf(@JvmSerializableLambda {})[0] + +val bad1 = @JvmSerializableLambda 1 +val bad2 = @JvmSerializableLambda object {} +val bad3 = @JvmSerializableLambda ::foo +val bad4 = listOf(@JvmSerializableLambda 1)[0] diff --git a/compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.txt b/compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.txt new file mode 100644 index 00000000000..39d9ddd60b1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.txt @@ -0,0 +1,11 @@ +package + +public val bad1: kotlin.Int +public val bad2: kotlin.Any +public val bad3: kotlin.reflect.KFunction0<() -> kotlin.Unit> +public val bad4: kotlin.Int +public val good1: () -> kotlin.Unit +public val good2: () -> kotlin.Unit +public val good3: kotlin.Any.() -> kotlin.Unit +public val good4: () -> kotlin.Unit +public fun foo(): () -> kotlin.Unit diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 91965bb591a..0f650c30ca2 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -1296,6 +1296,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/annotations/javaAnnotationWithClassArray.kt"); } + @Test + @TestMetadata("JvmSerializableLambdaAnnotation.kt") + public void testJvmSerializableLambdaAnnotation() throws Exception { + runTest("compiler/testData/diagnostics/tests/annotations/JvmSerializableLambdaAnnotation.kt"); + } + @Test @TestMetadata("kt1860-negative.kt") public void testKt1860_negative() throws Exception { 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 34a503cc722..873a03552da 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 @@ -26346,6 +26346,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/invokedynamic/lambdas/genericLambdaSignature.kt"); } + @Test + @TestMetadata("jvmSerializableLambda.kt") + public void testJvmSerializableLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/lambdas/jvmSerializableLambda.kt"); + } + @Test @TestMetadata("kt52875.kt") public void testKt52875() throws Exception { @@ -42140,6 +42146,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/reflection/lambdaClasses"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); } + @Test + @TestMetadata("lightweightLambdas.kt") + public void testLightweightLambdas() throws Exception { + runTest("compiler/testData/codegen/box/reflection/lambdaClasses/lightweightLambdas.kt"); + } + @Test @TestMetadata("parameterNamesAndNullability.kt") public void testParameterNamesAndNullability() throws Exception { diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index ad23a91047d..83ac1e0ca9b 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -264,6 +264,7 @@ enum class LanguageFeature( RangeUntilOperator(KOTLIN_1_8), // KT-15613 GenericInlineClassParameter(sinceVersion = KOTLIN_1_8, kind = UNSTABLE_FEATURE), // KT-32162 DataObjects(KOTLIN_1_8), // KT-4107 + LightweightLambdas(KOTLIN_1_8, defaultState = State.DISABLED), // 1.9 diff --git a/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt b/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt index a2adce99050..e06ae75c082 100644 --- a/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt +++ b/libraries/stdlib/common/src/kotlin/JvmAnnotationsH.kt @@ -166,3 +166,13 @@ public expect annotation class Synchronized() @SinceKotlin("1.2") @OptionalExpectation internal expect annotation class JvmPackageName(val name: String) + + +/** + * Makes the annotated lambda function `java.io.Serializable`, generates a pretty `toString` implementation and adds reflection metadata. + */ +@Target(EXPRESSION) +@Retention(AnnotationRetention.SOURCE) +@SinceKotlin("1.8") +@OptionalExpectation +public expect annotation class JvmSerializableLambda diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmFlagAnnotations.kt b/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmFlagAnnotations.kt index 4e788606a56..8c93865342f 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmFlagAnnotations.kt +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/annotations/JvmFlagAnnotations.kt @@ -43,4 +43,13 @@ public actual annotation class Strictfp @Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented -public actual annotation class Synchronized \ No newline at end of file +public actual annotation class Synchronized + +/** + * Makes the annotated lambda function implement `java.io.Serializable`, + * generates a pretty `toString` implementation and adds reflection metadata. + */ +@Target(EXPRESSION) +@Retention(AnnotationRetention.SOURCE) +@SinceKotlin("1.8") +public actual annotation class JvmSerializableLambda \ No newline at end of file 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 7688be4f054..146c345db65 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 @@ -3309,6 +3309,9 @@ public abstract interface annotation class kotlin/jvm/JvmOverloads : java/lang/a public abstract interface annotation class kotlin/jvm/JvmRecord : java/lang/annotation/Annotation { } +public abstract interface annotation class kotlin/jvm/JvmSerializableLambda : java/lang/annotation/Annotation { +} + public abstract interface annotation class kotlin/jvm/JvmStatic : java/lang/annotation/Annotation { }