From 529b52676382ddcbf14532d474763eac9f964725 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 1 Feb 2017 19:00:01 +0300 Subject: [PATCH] Support parameter destructuring in suspend lambdas #KT-16092 Fixed --- .../codegen/ClosureGenerationStrategy.kt | 33 +------------- .../jetbrains/kotlin/codegen/codegenUtil.kt | 35 +++++++++++++-- .../codegen/coroutines/CoroutineCodegen.kt | 4 ++ .../destructuringInLambdas.kt | 23 ++++++++++ .../destructuringInLambdas.txt | 44 +++++++++++++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 6 +++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 +++ ...LightAnalysisModeCodegenTestGenerated.java | 6 +++ .../semantics/JsCodegenBoxTestGenerated.java | 12 +++++ 9 files changed, 133 insertions(+), 36 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt create mode 100644 compiler/testData/codegen/light-analysis/coroutines/featureIntersection/destructuringInLambdas.txt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureGenerationStrategy.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureGenerationStrategy.kt index 20d901558b7..bde19968763 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureGenerationStrategy.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClosureGenerationStrategy.kt @@ -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 - } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index f4f4271d88b..8b36c829db4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -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) { + 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 +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt index 974b6d11b4a..26a03c0fe6a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -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() = diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt new file mode 100644 index 00000000000..5cc13adad3e --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/destructuringInLambdas.kt @@ -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 +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/featureIntersection/destructuringInLambdas.txt b/compiler/testData/codegen/light-analysis/coroutines/featureIntersection/destructuringInLambdas.txt new file mode 100644 index 00000000000..5d2746910a7 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/featureIntersection/destructuringInLambdas.txt @@ -0,0 +1,44 @@ +@kotlin.Metadata +public final class A { + private final @org.jetbrains.annotations.NotNull field o: java.lang.String + public method (@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 (): void + public method (@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.CoroutineContext): void + public synthetic method (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 (): void +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 7ae71e33d7c..7dc58bd75e6 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index bd95d34fdc7..763e8cfa41a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 0f8eddedcd6..5b021b21055 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -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"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 823ce425824..3c856630e61 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -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");