From 4128d2751004aa34a54e1ab35daaf7e360a749f0 Mon Sep 17 00:00:00 2001 From: pyos Date: Tue, 1 Jun 2021 11:52:30 +0200 Subject: [PATCH] JVM_IR: do not use invokedynamic for inline fun references --- .../jvm/lower/indy/LambdaMetafactoryArguments.kt | 12 ++++++++---- .../codegen/box/invokedynamic/sam/inlineOnly.kt | 14 ++++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt index 93465d64943..1c73860d41f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JvmAnnotationNames import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.inline.INLINE_ONLY_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.utils.addIfNotNull @@ -76,20 +75,25 @@ internal class LambdaMetafactoryArgumentsBuilder( if (samClass.requiresDelegationToDefaultImpls()) return null + // TODO in cases where LambdaMetafactory is unusable directly due to problems with the implementation function, + // we can still avoid generating an entire class by converting the function reference into a lambda first; + // see `InlineCallableReferenceToLambdaPhase` val implFun = reference.symbol.owner // Don't generate references to intrinsic functions as invokedynamic (no such method exists at run-time). if (context.irIntrinsics.getIntrinsic(implFun.symbol) != null) return null - // Don't generate reference to inlineOnly methods as those do not exist at runtime. - if (implFun.hasAnnotation(INLINE_ONLY_ANNOTATION_FQ_NAME)) + // Can't use invokedynamic if the referenced function has to be inlined for correct semantics + // Also in some cases like `private inline fun` we'd need accessors, which `SyntheticAccessorLowering` + // won't generate under the assumption that the inline function will be inlined. Plus if the function + // is in a different module we should probably copy it anyway (and regenerate all objects in it). + if (implFun.isInline) return null if (implFun is IrConstructor && implFun.visibility.isPrivate) { // Kotlin generates constructor accessors differently from Java. // TODO more precise accessibility check (see SyntheticAccessorLowering::isAccessible) - // TODO wrap inaccessible constructor with a local function return null } diff --git a/compiler/testData/codegen/box/invokedynamic/sam/inlineOnly.kt b/compiler/testData/codegen/box/invokedynamic/sam/inlineOnly.kt index 2cf07f19a0c..0cada45869f 100644 --- a/compiler/testData/codegen/box/invokedynamic/sam/inlineOnly.kt +++ b/compiler/testData/codegen/box/invokedynamic/sam/inlineOnly.kt @@ -10,11 +10,17 @@ import java.util.function.Consumer -fun call(c: Consumer) { -} +inline fun f(x: T) = + println("${T::class.simpleName}($x)") + +private inline fun g(x: String) = println(x) + +fun call(c: Consumer) = c.accept("") fun box(): String { - // println is inline only and therefore we cannot use invoke-dynamic to target it. - call(::println) + call(::println) // `println` is `@InlineOnly` + call(::f) // `f` has a reified type parameter and thus isn't callable directly + val obj = { call(::g) } // `g` is inaccessible in this scope + obj() return "OK" }