From 4fc1bd9ec575312857ca433ecf548c7dc147c591 Mon Sep 17 00:00:00 2001 From: pyos Date: Thu, 31 Oct 2019 10:56:05 +0100 Subject: [PATCH] Support inlining functions with KT-28064 style objects Namely, anonymous objects defined in lambdas that have all captured variables as loose fields instead of a single reference to the parent. The question is, when a lambda inside an inline function defines an anonymous object, and that object is not regenerated during codegen for the inline function itself, but then has to be regenerated at call site anyway, do we use an outer `this` or loose capture fields? For example, before KT-28064: inline fun f1(g: () -> Unit) = object { g() } // -> f1$1 { $g: () -> Unit } inline fun f2(g: () -> Unit) = f1 { object { g() } } // -> f2$$inlined$f1$1 { $g: () -> Unit } // f2$$inlined$f1$1$lambda$1 { this$0: f2$$inlined$f1$1 } inline fun f3(g: () -> Unit) = f2 { object { g() } } // -> f3$$inlined$f2$1 { $g: () -> Unit } // f3$$inlined$f2$1$1 { this$0: f3$$inlined$f2$1 } // f3$$inlined$f2$1$1$lambda$1 { this$0: f3$$inlined$f2$1$1 } After KT-28064: inline fun f2(g: () -> Unit) = f1 { object { g() } } // -> f2$$inlined$f1$1 { $g: () -> Unit } // f2$1$1 { $g: () -> Unit } inline fun f3(g: () -> Unit) = f2 { object { g() } } // -> f3$$inlined$f2$1 { $g: () -> Unit } // f3$$inlined$f2$2 { ??? } // f3$1$1 { $g: () -> Unit } Should `???` be `this$0: f3$$inlined$f2$1` or `$g: () -> Unit`? This commit chooses the latter for KT-28064 bytecode and keeps `this$0` when inlining the old bytecode. --- .../inline/AnonymousObjectTransformer.kt | 37 +++++++++++++------ .../boxInline/anonymousObject/defineClass.kt | 2 - .../properRecapturing/inlineChain.kt | 2 - .../properRecapturingInClass/inlineChain.kt | 2 - .../noCapturedThisOnCallSite.kt | 2 - .../twoInlineLambdaComplex.kt | 2 - .../twoInlineLambdaComplex_2.kt | 2 - .../builders/buildersAndLambdaCapturing.kt | 2 - ...nsformedConstructorWithAdditionalObject.kt | 2 - .../noInlineLambdaChainWithCapturedInline.kt | 2 - .../codegen/bytecodeText/kt10259_2.kt | 1 - 11 files changed, 25 insertions(+), 31 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt index 5fc732ff529..9b470f5bb51 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt @@ -494,13 +494,27 @@ class AnonymousObjectTransformer( //For all inlined lambdas add their captured parameters //TODO: some of such parameters could be skipped - we should perform additional analysis - val capturedLambdasToInline = HashMap() //captured var of inlined parameter val allRecapturedParameters = ArrayList() - val addCapturedNotAddOuter = - parentFieldRemapper.isRoot || parentFieldRemapper is InlinedLambdaRemapper && parentFieldRemapper.parent!!.isRoot - val alreadyAdded = HashMap() - for (info in capturedLambdas) { - if (addCapturedNotAddOuter) { + if (parentFieldRemapper !is InlinedLambdaRemapper || parentFieldRemapper.parent!!.isRoot) { + // Possible cases: + // + // 1. Top-level object in an inline lambda that is *not* being inlined into another object. In this case, we + // have no choice but to add a separate field for each captured variable. `capturedLambdas` is either empty + // (already have the fields) or contains the parent lambda object (captures used to be read from it, but + // the object will be removed and its contents inlined). + // + // 2. Top-level object in a named inline function. Again, there's no option but to add separate fields. + // `capturedLambdas` contains all lambdas used by this object and nested objects. + // + // 3. Nested object, either in an inline lambda or an inline function. This case has two subcases: + // * The object's captures are passed as separate arguments (e.g. KT-28064 style object that used to be in a lambda); + // we could group them into `this$0` now, but choose not to. Lambdas are replaced by their captures. + // * The object's captures are already grouped into `this$0`; this includes captured lambda parameters (for objects in + // inline functions) and a reference to the outer object or lambda (for objects in lambdas), so `capturedLambdas` is + // empty and the choice doesn't matter. + // + val alreadyAdded = HashMap() + for (info in capturedLambdas) { for (desc in info.capturedVars) { val key = desc.fieldName + "$$$" + desc.type.className val alreadyAddedParam = alreadyAdded[key] @@ -532,11 +546,10 @@ class AnonymousObjectTransformer( } } } - capturedLambdasToInline.put(info.lambdaClassType.internalName, info) - } - - if (parentFieldRemapper is InlinedLambdaRemapper && !capturedLambdas.isEmpty() && !addCapturedNotAddOuter) { - //lambda with non InlinedLambdaRemapper already have outer + } else if (capturedLambdas.isNotEmpty()) { + // Top-level object in a lambda inlined into another object. As already said above, either `capturedLambdas` is empty + // (no captures or captures were generated as loose fields) or it contains a single entry for the parent lambda itself. + // Simply replace one `this$0` (of lambda type) with another (of destination object type). val parent = parentFieldRemapper.parent as? RegeneratedLambdaFieldRemapper ?: throw AssertionError("Expecting RegeneratedLambdaFieldRemapper, but ${parentFieldRemapper.parent}") val ownerType = Type.getObjectType(parent.originalLambdaInternalName) @@ -550,7 +563,7 @@ class AnonymousObjectTransformer( } transformationInfo.allRecapturedParameters = allRecapturedParameters - transformationInfo.capturedLambdasToInline = capturedLambdasToInline + transformationInfo.capturedLambdasToInline = capturedLambdas.associateBy { it.lambdaClassType.internalName } return constructorAdditionalFakeParams } diff --git a/compiler/testData/codegen/boxInline/anonymousObject/defineClass.kt b/compiler/testData/codegen/boxInline/anonymousObject/defineClass.kt index a8caa4d1e97..788602e4de7 100644 --- a/compiler/testData/codegen/boxInline/anonymousObject/defineClass.kt +++ b/compiler/testData/codegen/boxInline/anonymousObject/defineClass.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: 1.kt package test diff --git a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturing/inlineChain.kt b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturing/inlineChain.kt index 5e29220edef..ff2504b3164 100644 --- a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturing/inlineChain.kt +++ b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturing/inlineChain.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: 1.kt package test diff --git a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/inlineChain.kt b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/inlineChain.kt index ebb81f56176..324fa7fb4b2 100644 --- a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/inlineChain.kt +++ b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/inlineChain.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: 1.kt package test diff --git a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/noCapturedThisOnCallSite.kt b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/noCapturedThisOnCallSite.kt index ef81b2b6b9f..867a8a040bb 100644 --- a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/noCapturedThisOnCallSite.kt +++ b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/noCapturedThisOnCallSite.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: 1.kt package test diff --git a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex.kt b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex.kt index 7f672a0cdde..2459a2f67e6 100644 --- a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex.kt +++ b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: 1.kt package test diff --git a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex_2.kt b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex_2.kt index eac9babc926..472657d058b 100644 --- a/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex_2.kt +++ b/compiler/testData/codegen/boxInline/anonymousObject/properRecapturingInClass/twoInlineLambdaComplex_2.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: 1.kt package test diff --git a/compiler/testData/codegen/boxInline/builders/buildersAndLambdaCapturing.kt b/compiler/testData/codegen/boxInline/builders/buildersAndLambdaCapturing.kt index 7c10156b035..e52fd9a3e72 100644 --- a/compiler/testData/codegen/boxInline/builders/buildersAndLambdaCapturing.kt +++ b/compiler/testData/codegen/boxInline/builders/buildersAndLambdaCapturing.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // TARGET_BACKEND: JVM // FILE: 1.kt // WITH_RUNTIME diff --git a/compiler/testData/codegen/boxInline/enclosingInfo/transformedConstructorWithAdditionalObject.kt b/compiler/testData/codegen/boxInline/enclosingInfo/transformedConstructorWithAdditionalObject.kt index e449650bb4e..b453fa0cf19 100644 --- a/compiler/testData/codegen/boxInline/enclosingInfo/transformedConstructorWithAdditionalObject.kt +++ b/compiler/testData/codegen/boxInline/enclosingInfo/transformedConstructorWithAdditionalObject.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // TARGET_BACKEND: JVM // FILE: 1.kt // WITH_REFLECT diff --git a/compiler/testData/codegen/boxInline/noInline/noInlineLambdaChainWithCapturedInline.kt b/compiler/testData/codegen/boxInline/noInline/noInlineLambdaChainWithCapturedInline.kt index 258177f4002..133f57d8324 100644 --- a/compiler/testData/codegen/boxInline/noInline/noInlineLambdaChainWithCapturedInline.kt +++ b/compiler/testData/codegen/boxInline/noInline/noInlineLambdaChainWithCapturedInline.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND_MULTI_MODULE: JVM_IR // FILE: 1.kt package test diff --git a/compiler/testData/codegen/bytecodeText/kt10259_2.kt b/compiler/testData/codegen/bytecodeText/kt10259_2.kt index 727fe26fb55..a7bf15def8d 100644 --- a/compiler/testData/codegen/bytecodeText/kt10259_2.kt +++ b/compiler/testData/codegen/bytecodeText/kt10259_2.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun box(): String { var encl1 = "fail"; test {