diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index 036bc0650f1..e0fbc650e86 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -53,6 +53,6 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE : IrDeclarationOriginImpl("FOR_INLINE_TEMPLATE_CROSSINLINE") object CONTINUATION_CLASS_RESULT_FIELD: IrDeclarationOriginImpl("CONTINUATION_CLASS_RESULT_FIELD", isSynthetic = true) object COMPANION_PROPERTY_BACKING_FIELD : IrDeclarationOriginImpl("COMPANION_MOVED_PROPERTY_BACKING_FIELD") - object FIELD_FOR_STATIC_LAMBDA_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_STATIC_LAMBDA_INSTANCE") + object FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE") object ABSTRACT_BRIDGE_STUB : IrDeclarationOriginImpl("ABSTRACT_BRIDGE_STUB") } 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 9d30b4bb0e4..00fc7ac5e7d 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 @@ -315,7 +315,7 @@ private val jvmFilePhases = listOf( returnableBlocksPhase, localDeclarationsPhase, jvmLocalClassExtractionPhase, - staticLambdaPhase, + staticCallableReferencePhase, jvmDefaultConstructorPhase, 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 8cf2f2beed6..997a9187112 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 @@ -13,8 +13,8 @@ import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.config.LanguageFeature -import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.DescriptorVisibility +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.builders.declarations.buildFun import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.toIrBasedDescriptor @@ -270,7 +270,7 @@ abstract class ClassCodegen protected constructor( if (field.origin != JvmLoweredDeclarationOrigin.CONTINUATION_CLASS_RESULT_FIELD) { val skipNullabilityAnnotations = flags and (Opcodes.ACC_SYNTHETIC or Opcodes.ACC_ENUM) != 0 || - field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_LAMBDA_INSTANCE + field.origin == JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE object : AnnotationCodegen(this@ClassCodegen, context, skipNullabilityAnnotations) { override fun visitAnnotation(descr: String?, visible: Boolean): AnnotationVisitor { return fv.visitAnnotation(descr, visible) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticLambdaLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticCallableReferenceLowering.kt similarity index 85% rename from compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticLambdaLowering.kt rename to compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticCallableReferenceLowering.kt index ebd78d69abd..20206ccaad8 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticLambdaLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/StaticCallableReferenceLowering.kt @@ -35,21 +35,21 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.util.* -internal val staticLambdaPhase = makeIrFilePhase( - ::StaticLambdaLowering, - name = "StaticLambdaPhase", +internal val staticCallableReferencePhase = makeIrFilePhase( + ::StaticCallableReferenceLowering, + name = "StaticCallableReferencePhase", description = "Turn static callable references into singletons" ) -class StaticLambdaLowering(val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() { - private val staticLambdaFields = HashMap() +class StaticCallableReferenceLowering(val backendContext: JvmBackendContext) : FileLoweringPass, IrElementTransformerVoid() { + private val staticInstanceFields = HashMap() override fun lower(irFile: IrFile) = irFile.transformChildrenVoid() override fun visitClass(declaration: IrClass): IrStatement { declaration.transformChildrenVoid() if (declaration.isSyntheticSingleton) { - declaration.declarations += getFieldForStaticLambdaInstance(declaration).also { field -> + declaration.declarations += getFieldForStaticCallableReferenceInstance(declaration).also { field -> field.initializer = backendContext.createIrBuilder(field.symbol).run { irExprBody(irCall(declaration.primaryConstructor!!)) } @@ -58,17 +58,17 @@ class StaticLambdaLowering(val backendContext: JvmBackendContext) : FileLowering return declaration } - private fun getFieldForStaticLambdaInstance(lambdaClass: IrClass): IrField = - staticLambdaFields.getOrPut(lambdaClass) { + private fun getFieldForStaticCallableReferenceInstance(irClass: IrClass): IrField = + staticInstanceFields.getOrPut(irClass) { backendContext.irFactory.buildField { name = Name.identifier(JvmAbi.INSTANCE_FIELD) - type = lambdaClass.defaultType - origin = JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_LAMBDA_INSTANCE + type = irClass.defaultType + origin = JvmLoweredDeclarationOrigin.FIELD_FOR_STATIC_CALLABLE_REFERENCE_INSTANCE isFinal = true isStatic = true visibility = DescriptorVisibilities.PUBLIC }.apply { - parent = lambdaClass + parent = irClass } } @@ -77,14 +77,16 @@ class StaticLambdaLowering(val backendContext: JvmBackendContext) : FileLowering if (!constructor.constructedClass.isSyntheticSingleton) return super.visitConstructorCall(expression) - val instanceField = getFieldForStaticLambdaInstance(constructor.constructedClass) + val instanceField = getFieldForStaticCallableReferenceInstance(constructor.constructedClass) return IrGetFieldImpl(expression.startOffset, expression.endOffset, instanceField.symbol, expression.type) } // Recognize callable references with no value or type arguments. The only type arguments in Kotlin stem from usages of // reified type parameters, which we unfortunately don't record as parameters so we have to check the body of the class. private val IrClass.isSyntheticSingleton: Boolean - get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL || origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL) + get() = (origin == JvmLoweredDeclarationOrigin.LAMBDA_IMPL + || origin == JvmLoweredDeclarationOrigin.FUNCTION_REFERENCE_IMPL + || origin == JvmLoweredDeclarationOrigin.GENERATED_PROPERTY_REFERENCE) && primaryConstructor!!.valueParameters.isEmpty() && !containsReifiedTypeParameters diff --git a/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt index 820f6a5c951..61fddc4b17f 100644 --- a/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt +++ b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR class A { fun foo() {} val bar = 42 diff --git a/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses_ir.txt b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses_ir.txt new file mode 100644 index 00000000000..5db50865986 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/noReceiverInCallableReferenceClasses_ir.txt @@ -0,0 +1,68 @@ +@kotlin.Metadata +public final class A { + // source: 'noReceiverInCallableReferenceClasses.kt' + private final field bar: int + public method (): void + public final method foo(): void + public final method getBar(): int +} + +@kotlin.Metadata +synthetic final class NoReceiverInCallableReferenceClassesKt$A_bar$1 { + // source: 'noReceiverInCallableReferenceClasses.kt' + enclosing method NoReceiverInCallableReferenceClassesKt.()V + public final static field INSTANCE: NoReceiverInCallableReferenceClassesKt$A_bar$1 + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_bar$1 + static method (): void + method (): void + public @org.jetbrains.annotations.Nullable method get(@org.jetbrains.annotations.Nullable p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +synthetic final class NoReceiverInCallableReferenceClassesKt$A_foo$1 { + // source: 'noReceiverInCallableReferenceClasses.kt' + enclosing method NoReceiverInCallableReferenceClassesKt.()V + public final static field INSTANCE: NoReceiverInCallableReferenceClassesKt$A_foo$1 + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_foo$1 + static method (): void + method (): void + public final method invoke(@org.jetbrains.annotations.NotNull p0: A): void + public synthetic bridge method invoke(p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +synthetic final class NoReceiverInCallableReferenceClassesKt$aBar$1 { + // source: 'noReceiverInCallableReferenceClasses.kt' + enclosing method NoReceiverInCallableReferenceClassesKt.()V + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aBar$1 + method (p0: java.lang.Object): void + public @org.jetbrains.annotations.Nullable method get(): java.lang.Object +} + +@kotlin.Metadata +synthetic final class NoReceiverInCallableReferenceClassesKt$aFoo$1 { + // source: 'noReceiverInCallableReferenceClasses.kt' + enclosing method NoReceiverInCallableReferenceClassesKt.()V + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aFoo$1 + method (p0: A): void + public synthetic bridge method invoke(): java.lang.Object + public final method invoke(): void +} + +@kotlin.Metadata +public final class NoReceiverInCallableReferenceClassesKt { + // source: 'noReceiverInCallableReferenceClasses.kt' + private final static @org.jetbrains.annotations.NotNull field A_bar: kotlin.reflect.KProperty1 + private final static @org.jetbrains.annotations.NotNull field A_foo: kotlin.reflect.KFunction + private final static @org.jetbrains.annotations.NotNull field aBar: kotlin.reflect.KProperty0 + private final static @org.jetbrains.annotations.NotNull field aFoo: kotlin.reflect.KFunction + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_bar$1 + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$A_foo$1 + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aBar$1 + inner (anonymous) class NoReceiverInCallableReferenceClassesKt$aFoo$1 + static method (): void + public final static @org.jetbrains.annotations.NotNull method getABar(): kotlin.reflect.KProperty0 + public final static @org.jetbrains.annotations.NotNull method getAFoo(): kotlin.reflect.KFunction + public final static @org.jetbrains.annotations.NotNull method getA_bar(): kotlin.reflect.KProperty1 + public final static @org.jetbrains.annotations.NotNull method getA_foo(): kotlin.reflect.KFunction +} diff --git a/compiler/testData/codegen/bytecodeText/callableReference/kt36975.kt b/compiler/testData/codegen/bytecodeText/callableReference/kt36975.kt new file mode 100644 index 00000000000..a39e0aec55c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/callableReference/kt36975.kt @@ -0,0 +1,10 @@ +// FILE: test.kt +class A(val value: String) + +fun box(): String { + val ref = A::value + return ref(A("OK")) +} + +// Check that non-bound callable references are generated as singletons +// 1 GETSTATIC TestKt\$box\$ref\$1.INSTANCE diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 74027e3f652..f258bba0e47 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -769,6 +769,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/callableReference/boundPropertyReferenceInInline.kt"); } + @TestMetadata("kt36975.kt") + public void testKt36975() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/callableReference/kt36975.kt"); + } + @TestMetadata("nameIntrinsicWithImplicitThis.kt") public void testNameIntrinsicWithImplicitThis() throws Exception { runTest("compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index 57d45823a6a..595608cd582 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -769,6 +769,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/callableReference/boundPropertyReferenceInInline.kt"); } + @TestMetadata("kt36975.kt") + public void testKt36975() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/callableReference/kt36975.kt"); + } + @TestMetadata("nameIntrinsicWithImplicitThis.kt") public void testNameIntrinsicWithImplicitThis() throws Exception { runTest("compiler/testData/codegen/bytecodeText/callableReference/nameIntrinsicWithImplicitThis.kt");