Support parameter destructuring in suspend lambdas

#KT-16092 Fixed
This commit is contained in:
Denis Zharkov
2017-02-01 19:00:01 +03:00
parent 0878049f15
commit 529b526763
9 changed files with 133 additions and 36 deletions
@@ -17,12 +17,8 @@
package org.jetbrains.kotlin.codegen
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
class ClosureGenerationStrategy(
state: GenerationState,
@@ -30,35 +26,8 @@ class ClosureGenerationStrategy(
) : FunctionGenerationStrategy.FunctionDefault(state, declaration) {
override fun doGenerateBody(codegen: ExpressionCodegen, signature: JvmMethodSignature) {
processDestructuringInLambdaParameters(codegen)
initializeVariablesForDestructuredLambdaParameters(codegen, codegen.context.functionDescriptor.valueParameters)
super.doGenerateBody(codegen, signature)
}
private fun processDestructuringInLambdaParameters(codegen: ExpressionCodegen) {
val savedIsShouldMarkLineNumbers = codegen.isShouldMarkLineNumbers
// Do not write line numbers until destructuring happens
// (otherwise destructuring variables will be uninitialized in the beginning of lambda)
codegen.isShouldMarkLineNumbers = false
for (parameterDescriptor in codegen.context.functionDescriptor.valueParameters) {
if (parameterDescriptor !is ValueParameterDescriptorImpl.WithDestructuringDeclaration) continue
for (entry in parameterDescriptor.destructuringVariables.filterOutDescriptorsWithSpecialNames()) {
codegen.myFrameMap.enter(entry, codegen.typeMapper.mapType(entry.type))
}
val destructuringDeclaration =
(DescriptorToSourceUtils.descriptorToDeclaration(parameterDescriptor) as? KtParameter)?.destructuringDeclaration
?: error("Destructuring declaration for descriptor $parameterDescriptor not found")
codegen.initializeDestructuringDeclarationVariables(
destructuringDeclaration,
TransientReceiver(parameterDescriptor.type),
codegen.findLocalOrCapturedValue(parameterDescriptor) ?: error("Local var not found for parameter $parameterDescriptor")
)
}
codegen.isShouldMarkLineNumbers = savedIsShouldMarkLineNumbers
}
}
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
import org.jetbrains.kotlin.diagnostics.rendering.RenderingContext
import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature.SpecialSignatureInfo
@@ -33,12 +34,10 @@ import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils.isSubclass
import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation
@@ -48,6 +47,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.serialization.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
@@ -348,3 +348,30 @@ fun MemberDescriptor.isToArrayFromCollection(): Boolean {
fun FqName.topLevelClassInternalName() = JvmClassName.byClassId(ClassId(parent(), shortName())).internalName
fun FqName.topLevelClassAsmType(): Type = Type.getObjectType(topLevelClassInternalName())
fun initializeVariablesForDestructuredLambdaParameters(codegen: ExpressionCodegen, valueParameters: List<ValueParameterDescriptor>) {
val savedIsShouldMarkLineNumbers = codegen.isShouldMarkLineNumbers
// Do not write line numbers until destructuring happens
// (otherwise destructuring variables will be uninitialized in the beginning of lambda)
codegen.isShouldMarkLineNumbers = false
for (parameterDescriptor in valueParameters) {
if (parameterDescriptor !is ValueParameterDescriptorImpl.WithDestructuringDeclaration) continue
for (entry in parameterDescriptor.destructuringVariables.filterOutDescriptorsWithSpecialNames()) {
codegen.myFrameMap.enter(entry, codegen.typeMapper.mapType(entry.type))
}
val destructuringDeclaration =
(DescriptorToSourceUtils.descriptorToDeclaration(parameterDescriptor) as? KtParameter)?.destructuringDeclaration
?: error("Destructuring declaration for descriptor $parameterDescriptor not found")
codegen.initializeDestructuringDeclarationVariables(
destructuringDeclaration,
TransientReceiver(parameterDescriptor.type),
codegen.findLocalOrCapturedValue(parameterDescriptor) ?: error("Local var not found for parameter $parameterDescriptor")
)
}
codegen.isShouldMarkLineNumbers = savedIsShouldMarkLineNumbers
}
@@ -280,6 +280,10 @@ class CoroutineCodegen private constructor(
val actualMethodStartLabel = Label()
v.visitLabel(actualMethodStartLabel)
context.setMethodStartLabel(actualMethodStartLabel)
if (isSuspendLambda) {
initializeVariablesForDestructuredLambdaParameters(this, originalSuspendFunctionDescriptor.valueParameters)
}
}
private fun allFunctionParameters() =
@@ -0,0 +1,23 @@
// WITH_RUNTIME
// WITH_COROUTINES
// IGNORE_BACKEND: JS
import kotlin.coroutines.experimental.*
import kotlin.coroutines.experimental.intrinsics.*
data class A(val o: String) {
operator fun component2(): String = "K"
}
fun builder(c: suspend (A) -> Unit) {
(c as (suspend A.() -> Unit)).startCoroutine(A("O"), EmptyContinuation)
}
fun box(): String {
var result = ""
builder {
(x, y) ->
result = x + y
}
return result
}
@@ -0,0 +1,44 @@
@kotlin.Metadata
public final class A {
private final @org.jetbrains.annotations.NotNull field o: java.lang.String
public method <init>(@org.jetbrains.annotations.NotNull p0: java.lang.String): void
public final @org.jetbrains.annotations.NotNull method component1(): java.lang.String
public final @org.jetbrains.annotations.NotNull method component2(): java.lang.String
public synthetic static method copy$default(p0: A, p1: java.lang.String, p2: int, p3: java.lang.Object): A
public final @org.jetbrains.annotations.NotNull method copy(@org.jetbrains.annotations.NotNull p0: java.lang.String): A
public method equals(p0: java.lang.Object): boolean
public final @org.jetbrains.annotations.NotNull method getO(): java.lang.String
public method hashCode(): int
public method toString(): java.lang.String
}
@kotlin.Metadata
public final class CoroutineUtilKt {
public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation
public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation
}
@kotlin.Metadata
public final class DestructuringInLambdasKt {
public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String
public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2): void
}
@kotlin.Metadata
public class EmptyContinuation {
public final static field Companion: EmptyContinuation.Companion
private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.experimental.CoroutineContext
inner class EmptyContinuation/Companion
public @synthetic.kotlin.jvm.GeneratedByJvmOverloads method <init>(): void
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.CoroutineContext): void
public synthetic method <init>(p0: kotlin.coroutines.experimental.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void
public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.experimental.CoroutineContext
public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void
public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void
}
@kotlin.Metadata
public final static class EmptyContinuation/Companion {
inner class EmptyContinuation/Companion
private method <init>(): void
}
@@ -5050,6 +5050,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("destructuringInLambdas.kt")
public void testDestructuringInLambdas() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt");
doTest(fileName);
}
@TestMetadata("tailrec.kt")
public void testTailrec() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
@@ -5050,6 +5050,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("destructuringInLambdas.kt")
public void testDestructuringInLambdas() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt");
doTest(fileName);
}
@TestMetadata("tailrec.kt")
public void testTailrec() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
@@ -5050,6 +5050,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("destructuringInLambdas.kt")
public void testDestructuringInLambdas() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt");
doTest(fileName);
}
@TestMetadata("tailrec.kt")
public void testTailrec() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
@@ -5735,6 +5735,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/featureIntersection"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("destructuringInLambdas.kt")
public void testDestructuringInLambdas() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt");
try {
doTest(fileName);
}
catch (Throwable ignore) {
return;
}
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("tailrec.kt")
public void testTailrec() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");