KT-45375 Lightweight lambdas; KT-52817 introduce @JvmSerializableLambda

This commit is contained in:
Pavel Mikhailovskii
2022-07-18 09:20:55 +02:00
parent 1557f919f5
commit 846537b367
33 changed files with 243 additions and 15 deletions
@@ -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 {
@@ -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)
@@ -16,8 +16,6 @@ enum class JvmClosureGenerationScheme(
;
companion object {
val DEFAULT = CLASS
@JvmStatic
fun fromString(string: String?): JvmClosureGenerationScheme? {
val lowerStr = string?.toLowerCaseAsciiOnly() ?: return null
@@ -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 {
@@ -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 {
@@ -33,5 +33,6 @@ object JvmExpressionCheckers : ExpressionCheckers() {
get() = setOf(
FirJavaAnnotationsChecker,
FirJvmPackageNameAnnotationsChecker,
FirJvmSerializableLambdaChecker,
)
}
@@ -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
)
}
}
}
}
@@ -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 {
@@ -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)
@@ -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<KtAnnotationEntry>,
actualTargets: List<KotlinTarget>,
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")
)
}
}
}
}
@@ -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) {
@@ -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())
}
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: IGNORED_IN_JS
// IGNORE_BACKEND: JS_IR
@@ -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"
}
@@ -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"
}
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,3 +1,4 @@
// LAMBDAS: CLASS
// !OPT_IN: kotlin.reflect.jvm.ExperimentalReflectionOnLambdas
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -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 = <!WRONG_ANNOTATION_TARGET!>@JvmSerializableLambda<!> 1
val bad2 = <!WRONG_ANNOTATION_TARGET!>@JvmSerializableLambda<!> object {}
val bad3 = <!WRONG_ANNOTATION_TARGET!>@JvmSerializableLambda<!> ::foo
val bad4 = listOf(<!WRONG_ANNOTATION_TARGET!>@JvmSerializableLambda<!> 1)[0]
@@ -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
@@ -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 {
@@ -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 {
@@ -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
@@ -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
@@ -43,4 +43,13 @@ public actual annotation class Strictfp
@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
public actual annotation class Synchronized
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
@@ -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 {
}