From d0d617b44e858f7478751fd3e2a97d78beebeb7b Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 17 Jan 2017 11:23:01 +0300 Subject: [PATCH] Fix signature mapping for default suspend function from interface Use jvm suspend function view in function codegen. Also use SUSPEND_FUNCTION_TO_JVM_VIEW slice if the value exists for the given key #KT-15552 Fixed --- .../kotlin/codegen/FunctionCodegen.java | 5 ++ .../kotlin/codegen/JvmRuntimeTypes.kt | 4 +- .../binding/CodegenAnnotatingVisitor.java | 2 +- .../codegen/coroutines/CoroutineCodegen.kt | 2 +- .../coroutines/coroutineCodegenUtil.kt | 20 ++++++-- .../codegen/state/KotlinTypeMapper.java | 10 ++-- .../SignaturesPropagationData.java | 4 ++ .../box/coroutines/suspendDefaultImpl.kt | 30 ++++++++++++ .../coroutines/suspendDefaultImpl.txt | 48 +++++++++++++++++++ .../tests/coroutines/suspendOverridability.kt | 2 +- .../ir/IrBlackBoxCodegenTestGenerated.java | 6 +++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 +++ ...LightAnalysisModeCodegenTestGenerated.java | 6 +++ .../semantics/JsCodegenBoxTestGenerated.java | 6 +++ 14 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt create mode 100644 compiler/testData/codegen/light-analysis/coroutines/suspendDefaultImpl.txt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index f097d85749b..f9952a28fc4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -167,6 +167,11 @@ public class FunctionCodegen { @NotNull FunctionDescriptor descriptor, @NotNull FunctionGenerationStrategy strategy ) { + if (CoroutineCodegenUtilKt.isSuspendFunctionNotSuspensionView(descriptor)) { + generateMethod(origin, CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(descriptor, bindingContext), strategy); + return; + } + generateMethod(origin, descriptor, owner.intoFunction(descriptor), strategy); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt index 2f8b49e24d0..44c48ab2119 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmRuntimeTypes.kt @@ -17,7 +17,7 @@ package org.jetbrains.kotlin.codegen import org.jetbrains.kotlin.builtins.createFunctionType -import org.jetbrains.kotlin.codegen.coroutines.createJvmSuspendFunctionView +import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView import org.jetbrains.kotlin.coroutines.isSuspendLambda import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations @@ -69,7 +69,7 @@ class JvmRuntimeTypes(module: ModuleDescriptor) { val actualFunctionDescriptor = if (descriptor.isSuspend) - createJvmSuspendFunctionView(descriptor) + getOrCreateJvmSuspendFunctionView(descriptor) else descriptor diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 3fa8e68dd50..f97d04fc525 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -431,7 +431,7 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid { if (functionDescriptor instanceof SimpleFunctionDescriptor && functionDescriptor.isSuspend()) { SimpleFunctionDescriptor jvmSuspendFunctionView = - CoroutineCodegenUtilKt.createJvmSuspendFunctionView( + CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView( (SimpleFunctionDescriptor) functionDescriptor ); 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 cca8ff3b679..a730bd50e8d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -330,7 +330,7 @@ class CoroutineCodegen( expressionCodegen, declaration, expressionCodegen.context.intoCoroutineClosure( - createJvmSuspendFunctionView(originalCoroutineLambdaDescriptor), + getOrCreateJvmSuspendFunctionView(originalCoroutineLambdaDescriptor, expressionCodegen.state.bindingContext), originalCoroutineLambdaDescriptor, expressionCodegen, expressionCodegen.state.typeMapper ), classBuilder, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt index 22d27b04460..feb7b754549 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -93,9 +93,7 @@ fun ResolvedCall<*>.replaceSuspensionFunctionWithRealDescriptor( val function = candidateDescriptor as? SimpleFunctionDescriptor ?: return null if (!function.isSuspend || function.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) != null) return null - val newCandidateDescriptor = - bindingContext.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, function) - ?: createJvmSuspendFunctionView(function) + val newCandidateDescriptor = getOrCreateJvmSuspendFunctionView(function, bindingContext) val newCall = ResolvedCallImpl( call, @@ -137,10 +135,24 @@ fun FunctionDescriptor.isStateMachineNeeded(bindingContext: BindingContext) = fun FunctionDescriptor.containsNonTailSuspensionCalls(bindingContext: BindingContext) = bindingContext[BindingContext.CONTAINS_NON_TAIL_SUSPEND_CALLS, original] == true + +fun CallableDescriptor.isSuspendFunctionNotSuspensionView(): Boolean { + if (this !is FunctionDescriptor) return false + return this.isSuspend && this.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) == null +} + // Suspend functions have irregular signatures on JVM, containing an additional last parameter with type `Continuation`, // and return type Any? // This function returns a function descriptor reflecting how the suspend function looks from point of view of JVM -fun createJvmSuspendFunctionView(function: D): D { +@JvmOverloads +fun getOrCreateJvmSuspendFunctionView(function: D, bindingContext: BindingContext? = null): D { + assert(function.isSuspend) { + "Suspended function is expected, but $function was found" + } + + @Suppress("UNCHECKED_CAST") + bindingContext?.get(CodegenBinding.SUSPEND_FUNCTION_TO_JVM_VIEW, function)?.let { return it as D } + val continuationParameter = ValueParameterDescriptorImpl( function, null, function.valueParameters.size, Annotations.EMPTY, Name.identifier("\$continuation"), // Add j.l.Object to invoke(), because that is the type of parameters we have in FunctionN+1 diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index eeb7dbc65ee..341177745d3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -378,10 +378,8 @@ public class KotlinTypeMapper { return Type.VOID_TYPE; } - if (descriptor instanceof SimpleFunctionDescriptor && ((SimpleFunctionDescriptor) descriptor).isSuspend() && - ((SimpleFunctionDescriptor) descriptor).getUserData( - CoroutineCodegenUtilKt.INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) == null) { - return mapReturnType(CoroutineCodegenUtilKt.createJvmSuspendFunctionView((SimpleFunctionDescriptor) descriptor), sw); + if (CoroutineCodegenUtilKt.isSuspendFunctionNotSuspensionView(descriptor)) { + return mapReturnType(CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView((SimpleFunctionDescriptor) descriptor), sw); } if (TypeSignatureMappingKt.hasVoidReturnType(descriptor)) { @@ -1029,6 +1027,10 @@ public class KotlinTypeMapper { } } + if (CoroutineCodegenUtilKt.isSuspendFunctionNotSuspensionView(f)) { + return mapSignature(CoroutineCodegenUtilKt.getOrCreateJvmSuspendFunctionView(f), kind, skipGenericSignature); + } + if (f instanceof ConstructorDescriptor) { return mapSignature(f, kind, f.getOriginal().getValueParameters(), skipGenericSignature); } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/kotlinSignature/SignaturesPropagationData.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/kotlinSignature/SignaturesPropagationData.java index c461a640cfa..a15e7fbbeb7 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/kotlinSignature/SignaturesPropagationData.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/kotlinSignature/SignaturesPropagationData.java @@ -212,6 +212,10 @@ public class SignaturesPropagationData { } for (FunctionDescriptor candidate : superFunctionCandidates) { + // Skip suspend super functions, because we doesn't process them correctly by now + // Moreover, we fail with exception sometimes + // TODO: remove this continue when KT-15747 is fixed + if (candidate.isSuspend()) continue; Method candidateSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(candidate); if (KotlinToJvmSignatureMapperKt.erasedSignaturesEqualIgnoringReturnTypes(autoSignature, candidateSignature)) { superFunctions.add(candidate); diff --git a/compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt b/compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt new file mode 100644 index 00000000000..abd50866c8d --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt @@ -0,0 +1,30 @@ +// WITH_RUNTIME +// WITH_COROUTINES +import kotlin.coroutines.startCoroutine +import kotlin.coroutines.intrinsics.* + +interface TestInterface { + suspend fun toInt(): Int = suspendCoroutineOrReturn { x -> + x.resume(56) + SUSPENDED_MARKER + } +} + +class TestClass2 : TestInterface { +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = -1 + builder { + result = TestClass2().toInt() + } + + if (result != 56) return "fail 1: $result" + + return "OK" +} + diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendDefaultImpl.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendDefaultImpl.txt new file mode 100644 index 00000000000..05090d78d87 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendDefaultImpl.txt @@ -0,0 +1,48 @@ +@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.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.Continuation +} + +@kotlin.Metadata +public class EmptyContinuation { + public final static field Companion: EmptyContinuation.Companion + private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.CoroutineContext + inner class EmptyContinuation/Companion + public @synthetic.kotlin.jvm.GeneratedByJvmOverloads method (): void + public method (@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.CoroutineContext): void + public synthetic method (p0: kotlin.coroutines.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void + public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.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 +} + +@kotlin.Metadata +public final class SuspendDefaultImplKt { + 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.Function1): void +} + +@kotlin.Metadata +public final class TestClass2 { + public method (): void + public @org.jetbrains.annotations.Nullable method toInt(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public interface TestInterface { + inner class TestInterface/DefaultImpls + public abstract @org.jetbrains.annotations.Nullable method toInt(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object +} + +@kotlin.Metadata +public final class TestInterface/DefaultImpls { + inner class TestInterface/DefaultImpls + public static @org.jetbrains.annotations.Nullable method toInt(@org.jetbrains.annotations.NotNull p0: TestInterface, p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendOverridability.kt b/compiler/testData/diagnostics/tests/coroutines/suspendOverridability.kt index fe6b33661c9..760479e3e96 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendOverridability.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendOverridability.kt @@ -25,7 +25,7 @@ interface C : A { } class D : J { - suspend override fun foo() { + suspend override fun foo() { } } 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 bea4a75fd92..c592290a591 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 @@ -4799,6 +4799,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("suspendDefaultImpl.kt") + public void testSuspendDefaultImpl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt"); + doTest(fileName); + } + @TestMetadata("suspendDelegation.kt") public void testSuspendDelegation() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDelegation.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index bcccf031b52..f0da36e51a5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4799,6 +4799,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("suspendDefaultImpl.kt") + public void testSuspendDefaultImpl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt"); + doTest(fileName); + } + @TestMetadata("suspendDelegation.kt") public void testSuspendDelegation() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDelegation.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 9e64d19d16b..77886a47ad5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -4799,6 +4799,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("suspendDefaultImpl.kt") + public void testSuspendDefaultImpl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt"); + doTest(fileName); + } + @TestMetadata("suspendDelegation.kt") public void testSuspendDelegation() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDelegation.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 ec025771776..12c62f2570d 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 @@ -5538,6 +5538,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("suspendDefaultImpl.kt") + public void testSuspendDefaultImpl() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDefaultImpl.kt"); + doTest(fileName); + } + @TestMetadata("suspendDelegation.kt") public void testSuspendDelegation() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendDelegation.kt");