JVM_IR: partially fix inline methods using captured crossinline lambdas
The fields containing crossinline lambdas should be package-private to
avoid generating synthetic accessors, which break object regeneration.
Note that the inline methods cannot actually be called, as call sites
will attempt to read the captured lambda from a field through a *copy*
of the local containing the object, so these reads will not be inlined,
causing an exception at runtime:
inline fun f(crossinline g: () -> Unit) = object : I {
inline fun h() = g()
// effectively `val tmp = this; return tmp.$g()`:
override fun run() = h()
}
f {}.run() // NoSuchFieldError: $g
This particular example can be fixed by reusing locals for receiver
parameters in IrInlineCodegen, but explicitly assigning `this` to
another variable and calling an inline method on it will break it again.
(This is only applicable to the JVM_IR backend, as the non-IR one fails
to generate `f` at all for some other reason.)
This commit is contained in:
+4
-1
@@ -66,6 +66,9 @@ interface VisibilityPolicy {
|
||||
fun forConstructor(declaration: IrConstructor, inInlineFunctionScope: Boolean): Visibility =
|
||||
Visibilities.PRIVATE
|
||||
|
||||
fun forCapturedField(value: IrValueSymbol): Visibility =
|
||||
Visibilities.PRIVATE
|
||||
|
||||
companion object {
|
||||
val DEFAULT = object : VisibilityPolicy {}
|
||||
}
|
||||
@@ -778,7 +781,7 @@ class LocalDeclarationsLowering(
|
||||
classDeclaration.startOffset,
|
||||
classDeclaration.endOffset,
|
||||
suggestNameForCapturedValue(owner, generatedNames),
|
||||
Visibilities.PRIVATE,
|
||||
visibilityPolicy.forCapturedField(capturedValue),
|
||||
classDeclaration,
|
||||
owner.type,
|
||||
owner is IrValueParameter && owner.isCrossinline
|
||||
|
||||
@@ -11,12 +11,13 @@ import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase
|
||||
import org.jetbrains.kotlin.backend.common.lower.optimizations.foldConstantLoweringPhase
|
||||
import org.jetbrains.kotlin.backend.common.phaser.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getJvmVisibilityOfDefaultArgumentStub
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.*
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
|
||||
import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor
|
||||
import org.jetbrains.kotlin.ir.util.isAnonymousObject
|
||||
import org.jetbrains.kotlin.ir.util.parentAsClass
|
||||
@@ -129,6 +130,12 @@ internal val localDeclarationsPhase = makeIrFilePhase<CommonBackendContext>(
|
||||
else
|
||||
declaration.visibility
|
||||
|
||||
override fun forCapturedField(value: IrValueSymbol): Visibility =
|
||||
if (value is IrValueParameterSymbol && value.owner.isCrossinline)
|
||||
JavaVisibilities.PACKAGE_VISIBILITY // avoid requiring a synthetic accessor for it
|
||||
else
|
||||
Visibilities.PRIVATE
|
||||
|
||||
private fun scopedVisibility(inInlineFunctionScope: Boolean): Visibility =
|
||||
if (inInlineFunctionScope) Visibilities.PUBLIC else JavaVisibilities.PACKAGE_VISIBILITY
|
||||
}
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// COMMON_COROUTINES_TEST
|
||||
// WITH_RUNTIME
|
||||
|
||||
+2
-2
@@ -89,7 +89,7 @@ public final class CrossinlineKt$consumeEach$2$send$1 {
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class CrossinlineKt$consumeEach$2 {
|
||||
private synthetic final field $action: kotlin.jvm.functions.Function2
|
||||
synthetic final @org.jetbrains.annotations.NotNull field $action: kotlin.jvm.functions.Function2
|
||||
inner class CrossinlineKt$consumeEach$2
|
||||
inner class CrossinlineKt$consumeEach$2$send$1
|
||||
public method <init>(p0: kotlin.jvm.functions.Function2): void
|
||||
@@ -355,7 +355,7 @@ public final class CrossinlineKt$source$1$consume$1 {
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class CrossinlineKt$source$1 {
|
||||
private synthetic final field $action: kotlin.jvm.functions.Function2
|
||||
synthetic final @org.jetbrains.annotations.NotNull field $action: kotlin.jvm.functions.Function2
|
||||
inner class CrossinlineKt$source$1
|
||||
inner class CrossinlineKt$source$1$consume$1
|
||||
public method <init>(p0: kotlin.jvm.functions.Function2): void
|
||||
|
||||
Vendored
+2
-2
@@ -65,7 +65,7 @@ public final class flow/InnerObjectRetransformationKt$collect$2$emit$1 {
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class flow/InnerObjectRetransformationKt$collect$2 {
|
||||
private synthetic final field $action: kotlin.jvm.functions.Function2
|
||||
synthetic final @org.jetbrains.annotations.NotNull field $action: kotlin.jvm.functions.Function2
|
||||
inner class flow/InnerObjectRetransformationKt$collect$2
|
||||
inner class flow/InnerObjectRetransformationKt$collect$2$emit$1
|
||||
public method <init>(p0: kotlin.jvm.functions.Function2): void
|
||||
@@ -86,7 +86,7 @@ public final class flow/InnerObjectRetransformationKt$flow$1$collect$1 {
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class flow/InnerObjectRetransformationKt$flow$1 {
|
||||
private synthetic final field $block: kotlin.jvm.functions.Function2
|
||||
synthetic final @org.jetbrains.annotations.NotNull field $block: kotlin.jvm.functions.Function2
|
||||
inner class flow/InnerObjectRetransformationKt$flow$1
|
||||
inner class flow/InnerObjectRetransformationKt$flow$1$collect$1
|
||||
public method <init>(p0: kotlin.jvm.functions.Function2): void
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public final class Override5Kt$inlineMe$1$generic$2 {
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class Override5Kt$inlineMe$1 {
|
||||
private synthetic final field $c: kotlin.jvm.functions.Function1
|
||||
synthetic final @org.jetbrains.annotations.NotNull field $c: kotlin.jvm.functions.Function1
|
||||
inner class Override5Kt$inlineMe$1
|
||||
inner class Override5Kt$inlineMe$1$generic$1
|
||||
inner class Override5Kt$inlineMe$1$generic$2
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// FILE: 1.kt
|
||||
package test
|
||||
|
||||
interface I {
|
||||
fun f(): String
|
||||
}
|
||||
|
||||
inline fun test(crossinline h: () -> String) = object : I {
|
||||
// TODO: actually call g() in f() -- currently, the inliner fails to detect
|
||||
// an inlined read of h's field because it uses a copy of `this`
|
||||
// as a receiver
|
||||
override fun f(): String = h()
|
||||
inline fun g(): String = h()
|
||||
}
|
||||
|
||||
// FILE: 2.kt
|
||||
|
||||
import test.*
|
||||
|
||||
fun box(): String = test { "OK" }.f()
|
||||
+2
-2
@@ -21,7 +21,7 @@ public final class TcoContinuationKt$collect$2$emit$1 {
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class TcoContinuationKt$collect$2 {
|
||||
private synthetic final field $action: kotlin.jvm.functions.Function2
|
||||
synthetic final @org.jetbrains.annotations.NotNull field $action: kotlin.jvm.functions.Function2
|
||||
inner class TcoContinuationKt$collect$2
|
||||
inner class TcoContinuationKt$collect$2$emit$1
|
||||
public method <init>(p0: kotlin.jvm.functions.Function2): void
|
||||
@@ -42,7 +42,7 @@ public final class TcoContinuationKt$flow$1$collect$1 {
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class TcoContinuationKt$flow$1 {
|
||||
private synthetic final field $block: kotlin.jvm.functions.Function2
|
||||
synthetic final @org.jetbrains.annotations.NotNull field $block: kotlin.jvm.functions.Function2
|
||||
inner class TcoContinuationKt$flow$1
|
||||
inner class TcoContinuationKt$flow$1$collect$1
|
||||
public method <init>(p0: kotlin.jvm.functions.Function2): void
|
||||
|
||||
+5
@@ -276,6 +276,11 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/superConstructorWithObjectParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInlineMethod.kt")
|
||||
public void testWithInlineMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/withInlineMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+5
@@ -276,6 +276,11 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/superConstructorWithObjectParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInlineMethod.kt")
|
||||
public void testWithInlineMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/withInlineMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+5
@@ -276,6 +276,11 @@ public class IrBlackBoxInlineCodegenTestGenerated extends AbstractIrBlackBoxInli
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/superConstructorWithObjectParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInlineMethod.kt")
|
||||
public void testWithInlineMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/withInlineMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+5
@@ -276,6 +276,11 @@ public class IrCompileKotlinAgainstInlineKotlinTestGenerated extends AbstractIrC
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/superConstructorWithObjectParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInlineMethod.kt")
|
||||
public void testWithInlineMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/withInlineMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+5
@@ -251,6 +251,11 @@ public class IrJsCodegenInlineTestGenerated extends AbstractIrJsCodegenInlineTes
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/superConstructorWithObjectParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInlineMethod.kt")
|
||||
public void testWithInlineMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/withInlineMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+5
@@ -251,6 +251,11 @@ public class JsCodegenInlineTestGenerated extends AbstractJsCodegenInlineTest {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/superConstructorWithObjectParameter.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("withInlineMethod.kt")
|
||||
public void testWithInlineMethod() throws Exception {
|
||||
runTest("compiler/testData/codegen/boxInline/anonymousObject/withInlineMethod.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user