JVM_IR: KT-40330 Unify field names for captured 'this' with JVM

NB some cases such as captured extension receiver for an extension
lambda are not supported yet; to be discussed, to what extent should we
actually follow JVM code shape here.
This commit is contained in:
Dmitry Petrov
2020-08-18 15:26:36 +03:00
parent 24bfc155af
commit 8f0aecce58
16 changed files with 371 additions and 22 deletions
@@ -6,7 +6,7 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.*
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedString
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor
@@ -31,11 +31,13 @@ import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.constructedClass
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
interface LocalNameProvider {
fun localName(declaration: IrDeclarationWithName): String =
@@ -637,7 +639,7 @@ class LocalDeclarationsLowering(
oldDeclaration: IrFunction,
newDeclaration: IrFunction
) = ArrayList<IrValueParameter>(capturedValues.size + oldDeclaration.valueParameters.size).apply {
val generatedNames = mutableSetOf<Name>()
val generatedNames = mutableSetOf<String>()
capturedValues.mapIndexedTo(this) { i, capturedValue ->
val p = capturedValue.owner
buildValueParameter(newDeclaration) {
@@ -743,7 +745,7 @@ class LocalDeclarationsLowering(
private fun createFieldsForCapturedValues(localClassContext: LocalClassContext) {
val classDeclaration = localClassContext.declaration
val generatedNames = mutableSetOf<Name>()
val generatedNames = mutableSetOf<String>()
localClassContext.closure.capturedValues.forEach { capturedValue ->
val owner = capturedValue.owner
@@ -772,18 +774,64 @@ class LocalDeclarationsLowering(
private fun Name.stripSpecialMarkers(): String =
if (isSpecial) asString().substring(1, asString().length - 1) else asString()
private fun suggestNameForCapturedValue(declaration: IrValueDeclaration, existing: MutableSet<Name>): Name {
private fun suggestNameForCapturedValue(declaration: IrValueDeclaration, usedNames: MutableSet<String>): Name {
if (declaration is IrValueParameter && declaration.name.asString() == "<this>") {
if (declaration.isDispatchReceiver()) {
return findFirstUnusedName("this\$0", usedNames) {
"this\$$it"
}
} else if (declaration.isExtensionReceiver()) {
val parentNameSuffix = declaration.parentNameSuffixForExtensionReceiver
return findFirstUnusedName("\$this_$parentNameSuffix", usedNames) {
"\$this_$parentNameSuffix\$$it"
}
}
// TODO captured extension receivers of extension lambdas?
}
val base = if (declaration.name.isSpecial)
declaration.name.stripSpecialMarkers()
else
declaration.name.asString()
var chosen = base.synthesizedName
var suffix = 0
while (!existing.add(chosen))
chosen = "$base$${++suffix}".synthesizedName
return chosen
return findFirstUnusedName(base.synthesizedString, usedNames) {
"$base$$it".synthesizedString
}
}
private inline fun findFirstUnusedName(initialName: String, usedNames: MutableSet<String>, nextName: (Int) -> String): Name {
var chosen = initialName
var suffix = 0
while (!usedNames.add(chosen))
chosen = nextName(++suffix)
return Name.identifier(chosen)
}
private fun IrValueParameter.isDispatchReceiver(): Boolean =
when (val parent = this.parent) {
is IrFunction ->
parent.dispatchReceiverParameter == this
is IrClass ->
parent.thisReceiver == this
else ->
false
}
private fun IrValueParameter.isExtensionReceiver(): Boolean {
val parentFun = parent as? IrFunction ?: return false
return parentFun.extensionReceiverParameter == this
}
private val IrValueParameter.parentNameSuffixForExtensionReceiver: String
get() {
val parentFun = parent as? IrSimpleFunction
?: throw AssertionError("Extension receiver parent is not a simple function: ${parent.render()}")
val correspondingProperty = parentFun.safeAs<IrSimpleFunction>()?.correspondingPropertySymbol?.owner
return when {
correspondingProperty != null ->
correspondingProperty.name.stripSpecialMarkers()
else ->
parentFun.name.stripSpecialMarkers()
}
}
private fun collectClosureForLocalDeclarations() {
//TODO: maybe use for granular declarations
@@ -26,7 +26,7 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1 {
@kotlin.Metadata
public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2 {
// source: 'crossinline.kt'
synthetic final field $this$inlined: Sink
synthetic final field $this_anonymous$inlined: Sink
inner (anonymous) class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
inner (anonymous) class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1
public method <init>(p0: Sink): void
@@ -37,7 +37,7 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2 {
@kotlin.Metadata
public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1 {
// source: 'crossinline.kt'
synthetic final field $this$inlined: SourceCrossinline
synthetic final field $this_filter$inlined: SourceCrossinline
inner (anonymous) class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1
inner (anonymous) class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$1
public method <init>(p0: SourceCrossinline): void
@@ -110,7 +110,7 @@ public final class CrossinlineKt$filter$$inlined$source$1$1 {
public final class CrossinlineKt$filter$$inlined$source$1 {
// source: 'crossinline.kt'
synthetic final field $predicate$inlined: kotlin.jvm.functions.Function1
synthetic final field $this$inlined: SourceCrossinline
synthetic final field $this_filter$inlined: SourceCrossinline
inner (anonymous) class CrossinlineKt$filter$$inlined$source$1
inner (anonymous) class CrossinlineKt$filter$$inlined$source$1$1
public method <init>(p0: SourceCrossinline, p1: kotlin.jvm.functions.Function1): void
@@ -134,7 +134,7 @@ public final class CrossinlineKt$filter$lambda-3$$inlined$consumeEach$1$1 {
public final class CrossinlineKt$filter$lambda-3$$inlined$consumeEach$1 {
// source: 'crossinline.kt'
synthetic final field $predicate$inlined: kotlin.jvm.functions.Function1
synthetic final field $this$inlined: Sink
synthetic final field $this_anonymous$inlined: Sink
inner (anonymous) class CrossinlineKt$filter$lambda-3$$inlined$consumeEach$1
inner (anonymous) class CrossinlineKt$filter$lambda-3$$inlined$consumeEach$1$1
public method <init>(p0: kotlin.jvm.functions.Function1, p1: Sink): void
@@ -41,7 +41,7 @@ public final class flow/InnerObjectRetransformationKt$check$$inlined$flow$1 {
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt$check$$inlined$flowWith$1 {
// source: 'innerObjectRetransformation.kt'
synthetic final field $this$inlined: flow.Flow
synthetic final field $this_flowWith$inlined: flow.Flow
inner (anonymous) class flow/InnerObjectRetransformationKt$check$$inlined$flowWith$1
public method <init>(p0: flow.Flow): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
@@ -119,7 +119,7 @@ public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1$1
public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1 {
// source: 'innerObjectRetransformation.kt'
synthetic final field $builderBlock$inlined: kotlin.jvm.functions.Function2
synthetic final field $this$inlined: flow.Flow
synthetic final field $this_flowWith$inlined: flow.Flow
inner (anonymous) class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1
inner (anonymous) class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1$1
public method <init>(p0: kotlin.jvm.functions.Function2, p1: flow.Flow): void
@@ -88,7 +88,7 @@ public final class TcoContinuationKt$foo$$inlined$map$1$2$1 {
@kotlin.Metadata
public final class TcoContinuationKt$foo$$inlined$map$1$2 {
// source: 'tcoContinuation.kt'
synthetic final field $this$inlined: FlowCollector
synthetic final field $this_anonymous$inlined: FlowCollector
inner (anonymous) class TcoContinuationKt$foo$$inlined$map$1$2
inner (anonymous) class TcoContinuationKt$foo$$inlined$map$1$2$1
public method <init>(p0: FlowCollector): void
@@ -98,7 +98,7 @@ public final class TcoContinuationKt$foo$$inlined$map$1$2 {
@kotlin.Metadata
public final class TcoContinuationKt$foo$$inlined$map$1 {
// source: 'tcoContinuation.kt'
synthetic final field $this$inlined: Flow
synthetic final field $this_transform$inlined: Flow
inner (anonymous) class TcoContinuationKt$foo$$inlined$map$1
public method <init>(p0: Flow): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
@@ -131,7 +131,7 @@ public final class TcoContinuationKt$map$$inlined$transform$1$2$1 {
@kotlin.Metadata
public final class TcoContinuationKt$map$$inlined$transform$1$2 {
// source: 'tcoContinuation.kt'
synthetic final field $this$inlined: FlowCollector
synthetic final field $this_anonymous$inlined: FlowCollector
synthetic final field $transformer$inlined$1: kotlin.jvm.functions.Function2
inner (anonymous) class TcoContinuationKt$map$$inlined$transform$1$2
inner (anonymous) class TcoContinuationKt$map$$inlined$transform$1$2$1
@@ -143,7 +143,7 @@ public final class TcoContinuationKt$map$$inlined$transform$1$2 {
@kotlin.Metadata
public final class TcoContinuationKt$map$$inlined$transform$1 {
// source: 'tcoContinuation.kt'
synthetic final field $this$inlined: Flow
synthetic final field $this_transform$inlined: Flow
synthetic final field $transformer$inlined$1: kotlin.jvm.functions.Function2
inner (anonymous) class TcoContinuationKt$map$$inlined$transform$1
inner (anonymous) class TcoContinuationKt$map$$inlined$transform$1$1
@@ -166,7 +166,7 @@ public final class TcoContinuationKt$transform$$inlined$flow$1$1 {
@kotlin.Metadata
public final class TcoContinuationKt$transform$$inlined$flow$1 {
// source: 'tcoContinuation.kt'
synthetic final field $this$inlined: Flow
synthetic final field $this_transform$inlined: Flow
synthetic final field $transformer$inlined: kotlin.jvm.functions.Function3
inner (anonymous) class TcoContinuationKt$transform$$inlined$flow$1
inner (anonymous) class TcoContinuationKt$transform$$inlined$flow$1$1
@@ -189,7 +189,7 @@ public final class TcoContinuationKt$transform$lambda-1$$inlined$collect$1$1 {
@kotlin.Metadata
public final class TcoContinuationKt$transform$lambda-1$$inlined$collect$1 {
// source: 'tcoContinuation.kt'
synthetic final field $this$inlined: FlowCollector
synthetic final field $this_anonymous$inlined: FlowCollector
synthetic final field $transformer$inlined: kotlin.jvm.functions.Function3
inner (anonymous) class TcoContinuationKt$transform$lambda-1$$inlined$collect$1
inner (anonymous) class TcoContinuationKt$transform$lambda-1$$inlined$collect$1$1
@@ -0,0 +1,22 @@
class Receiver {
fun foo() {}
}
fun useExtensionLambda(lambda: Receiver.() -> Unit) {
}
fun test() {
useExtensionLambda {
class NamedLocal {
fun run() {
foo()
}
}
}
}
// JVM_TEMPLATES
// 1 final synthetic LReceiver; \$this_useExtensionLambda
// JVM_IR_TEMPLATES
// 1 private final synthetic LReceiver; \$this
@@ -0,0 +1,17 @@
class Receiver {
fun foo() {}
}
fun Receiver.bar() {
class NamedLocal {
fun run() {
foo()
}
}
}
// JVM_TEMPLATES
// 1 final synthetic LReceiver; \$this_bar
// JVM_IR_TEMPLATES
// 1 private final synthetic LReceiver; \$this_bar
@@ -0,0 +1,23 @@
class Outer {
inner class Inner {
private fun bar() {
class NamedLocal {
fun run() {
innerFoo()
outerFoo()
}
}
}
fun innerFoo() {}
}
fun outerFoo() {}
}
// JVM_TEMPLATES
// 1 final synthetic LOuter\$Inner; this\$0
// JVM_IR_TEMPLATES
// 1 private final synthetic LOuter\$Inner; this\$0
// 1 private final synthetic LOuter; this\$1
@@ -0,0 +1,22 @@
class Receiver {
fun foo() {}
}
fun useExtensionLambda(lambda: Receiver.() -> Unit) {
}
fun test() {
useExtensionLambda label@ {
class NamedLocal {
fun run() {
foo()
}
}
}
}
// JVM_TEMPLATES
// 1 final synthetic LReceiver; \$this_label
// JVM_IR_TEMPLATES
// 1 private final synthetic LReceiver; \$this
@@ -0,0 +1,26 @@
class Foo {
fun foo() {}
}
class Bar {
fun bar() {}
}
fun Foo.test(bar: Bar) {
fun Bar.test() {
class Local {
fun run() {
foo()
bar()
}
}
}
}
// JVM_TEMPLATES
// 1 final synthetic LMultipleExtensionReceiversKt\$test\$1; this\$0
// 1 final synthetic LBar; \$this_test
// JVM_IR_TEMPLATES
// 1 private final synthetic LFoo; \$this_test
// 1 private final synthetic LBar; \$this_test\$1
@@ -0,0 +1,19 @@
class Outer {
inner class Inner {
private fun bar() {
class NamedLocal {
fun run() {
foo()
}
}
}
}
fun foo() {}
}
// JVM_TEMPLATES
// 1 final synthetic LOuter\$Inner; this\$0
// JVM_IR_TEMPLATES
// 1 private final synthetic LOuter; this\$0
@@ -0,0 +1,19 @@
class Outer {
inner class Inner {
constructor() {
class NamedLocal {
fun foo() {
outer()
}
}
}
}
fun outer() {}
}
// JVM_TEMPLATES
// 1 final synthetic LOuter\$Inner; this\$0
// JVM_IR_TEMPLATES
// 1 private final synthetic LOuter; this\$0
@@ -0,0 +1,19 @@
class Outer {
inner class Inner {
init {
class NamedLocal {
fun foo() {
outer()
}
}
}
}
fun outer() {}
}
// JVM_TEMPLATES
// 1 final synthetic LOuter\$Inner; this\$0
// JVM_IR_TEMPLATES
// 1 private final synthetic LOuter; this\$0
@@ -0,0 +1,17 @@
class Host {
private fun bar() {
class NamedLocal {
fun run() {
foo()
}
}
}
fun foo() {}
}
// JVM_TEMPLATES
// 1 final synthetic LHost; this\$0
// JVM_IR_TEMPLATES
// 1 private final synthetic LHost; this\$0
@@ -17,9 +17,10 @@ fun box(): String {
return `(X)`().`(Y)`()
}
// JVM_TEMPLATES
// One instance of each is in kotlin.Metadata.d2
// 1 \(X\)
// 1 \(Y\)
// JVM_IR_TEMPLATES
// 4 \$this
// 4 this\$0
@@ -1962,6 +1962,64 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FieldsForCapturedValues extends AbstractBytecodeTextTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath);
}
public void testAllFilesPresentInFieldsForCapturedValues() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("extensionLambdaExtensionReceiver.kt")
public void testExtensionLambdaExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/extensionLambdaExtensionReceiver.kt");
}
@TestMetadata("extensionReceiver.kt")
public void testExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/extensionReceiver.kt");
}
@TestMetadata("innerAndOuterThis.kt")
public void testInnerAndOuterThis() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/innerAndOuterThis.kt");
}
@TestMetadata("labeledExtensionLambdaExtensionReceiver.kt")
public void testLabeledExtensionLambdaExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/labeledExtensionLambdaExtensionReceiver.kt");
}
@TestMetadata("multipleExtensionReceivers.kt")
public void testMultipleExtensionReceivers() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/multipleExtensionReceivers.kt");
}
@TestMetadata("outerThis.kt")
public void testOuterThis() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/outerThis.kt");
}
@TestMetadata("outerThisInInnerConstructor.kt")
public void testOuterThisInInnerConstructor() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/outerThisInInnerConstructor.kt");
}
@TestMetadata("outerThisInInnerInitBlock.kt")
public void testOuterThisInInnerInitBlock() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/outerThisInInnerInitBlock.kt");
}
@TestMetadata("this.kt")
public void testThis() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/this.kt");
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/forLoop")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1917,6 +1917,64 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FieldsForCapturedValues extends AbstractIrBytecodeTextTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
}
public void testAllFilesPresentInFieldsForCapturedValues() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("extensionLambdaExtensionReceiver.kt")
public void testExtensionLambdaExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/extensionLambdaExtensionReceiver.kt");
}
@TestMetadata("extensionReceiver.kt")
public void testExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/extensionReceiver.kt");
}
@TestMetadata("innerAndOuterThis.kt")
public void testInnerAndOuterThis() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/innerAndOuterThis.kt");
}
@TestMetadata("labeledExtensionLambdaExtensionReceiver.kt")
public void testLabeledExtensionLambdaExtensionReceiver() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/labeledExtensionLambdaExtensionReceiver.kt");
}
@TestMetadata("multipleExtensionReceivers.kt")
public void testMultipleExtensionReceivers() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/multipleExtensionReceivers.kt");
}
@TestMetadata("outerThis.kt")
public void testOuterThis() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/outerThis.kt");
}
@TestMetadata("outerThisInInnerConstructor.kt")
public void testOuterThisInInnerConstructor() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/outerThisInInnerConstructor.kt");
}
@TestMetadata("outerThisInInnerInitBlock.kt")
public void testOuterThisInInnerInitBlock() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/outerThisInInnerInitBlock.kt");
}
@TestMetadata("this.kt")
public void testThis() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/fieldsForCapturedValues/this.kt");
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/forLoop")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)