diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt index a82160a8788..6b1b77fd1af 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/commonCoroutineCodegenUtil.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns val SUSPEND_WITH_CURRENT_CONTINUATION_NAME = Name.identifier("suspendWithCurrentContinuation") +val CONTINUATION_RESUME_METHOD_NAME = Name.identifier("resume") fun FunctionDescriptor.getBuiltInSuspendWithCurrentContinuation() = builtIns.builtInsCoroutinePackageFragment.getMemberScope() 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 77f6de693e0..cdd4379683b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineCodegen.kt @@ -265,7 +265,7 @@ class CoroutineCodegen( } private fun allLambdaParameters() = - coroutineLambdaDescriptor.extensionReceiverParameter.singletonOrEmptyList() + coroutineLambdaDescriptor.extensionReceiverParameter.singletonOrEmptyList() + coroutineLambdaDescriptor.valueParameters private fun ExpressionCodegen.generateLoadField(fieldInfo: FieldInfo) { StackValue.field(fieldInfo, generateThisOrOuter(context.thisDescriptor, false)).put(fieldInfo.fieldType, v) 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 c0f94101936..6b4d44da81b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutineCodegenUtil.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.coroutines import com.intellij.openapi.project.Project import org.jetbrains.kotlin.backend.common.SUSPEND_WITH_CURRENT_CONTINUATION_NAME import org.jetbrains.kotlin.backend.common.getBuiltInSuspendWithCurrentContinuation +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper import org.jetbrains.kotlin.descriptors.* @@ -32,10 +33,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.DelegatingBindingTrace import org.jetbrains.kotlin.resolve.DescriptorEquivalenceForOverrides -import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument -import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallImpl +import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns @@ -44,6 +42,7 @@ import org.jetbrains.kotlin.types.KotlinTypeFactory import org.jetbrains.kotlin.types.TypeConstructorSubstitution import org.jetbrains.kotlin.types.typeUtil.asTypeProjection import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -75,6 +74,19 @@ fun ResolvedCall<*>.replaceSuspensionFunctionWithRealDescriptor( project: Project, bindingContext: BindingContext ): ResolvedCallWithRealDescriptor? { + if (this is VariableAsFunctionResolvedCall) { + val replacedFunctionCall = + functionCall.replaceSuspensionFunctionWithRealDescriptor(project, bindingContext) + ?: return null + + @Suppress("UNCHECKED_CAST") + return replacedFunctionCall.copy( + VariableAsFunctionResolvedCallImpl( + replacedFunctionCall.resolvedCall as MutableResolvedCall, + variableCall as MutableResolvedCall + ) + ) + } val function = candidateDescriptor as? SimpleFunctionDescriptor ?: return null if (!function.isSuspend || function.getUserData(INITIAL_DESCRIPTOR_FOR_SUSPEND_FUNCTION) != null) return null @@ -119,7 +131,11 @@ fun ResolvedCall<*>.isSuspensionPoint(bindingContext: BindingContext) = fun createJvmSuspendFunctionView(function: D): D { val continuationParameter = ValueParameterDescriptorImpl( function, null, function.valueParameters.size, Annotations.EMPTY, Name.identifier("\$continuation"), - function.getContinuationParameterTypeOfSuspendFunction(), + // Add j.l.Object to invoke(), because that is the type of parameters we have in FunctionN+1 + if (function.containingDeclaration.safeAs()?.defaultType?.isBuiltinFunctionalType == true) + function.builtIns.nullableAnyType + else + function.getContinuationParameterTypeOfSuspendFunction(), /* declaresDefaultValue = */ false, /* isCrossinline = */ false, /* isNoinline = */ false, /* varargElementType = */ null, SourceElement.NO_SOURCE ) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index 1019b524af3..5a8400f6cc9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -220,9 +220,6 @@ class TypeResolver( val receiverTypeRef = type.receiverTypeReference val receiverType = if (receiverTypeRef == null) null else resolveType(c.noBareTypes(), receiverTypeRef) - if (hasSuspendModifier && type.parameters.isNotEmpty()) { - c.trace.report(UNSUPPORTED.on(type, "suspend function type with value parameters")) - } val parameterDescriptors = resolveParametersOfFunctionType(type.parameters) val returnTypeRef = type.returnTypeReference diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tasks/synthesizedInvokes.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tasks/synthesizedInvokes.kt index e42f0656abd..c75079694cb 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tasks/synthesizedInvokes.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tasks/synthesizedInvokes.kt @@ -18,25 +18,26 @@ package org.jetbrains.kotlin.resolve.calls.tasks import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor.Kind.Function +import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor.Kind.SuspendFunction import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.setSingleOverridden import org.jetbrains.kotlin.types.TypeSubstitutor import org.jetbrains.kotlin.util.OperatorNameConventions import java.util.* +private val BUILTIN_FUNCTIONS = setOf(Function, SuspendFunction) + fun createSynthesizedInvokes(functions: Collection): Collection { val result = ArrayList(1) for (invoke in functions) { if (invoke !is FunctionInvokeDescriptor || invoke.getValueParameters().isEmpty()) continue - val synthesized = if ((invoke.getContainingDeclaration() as? FunctionClassDescriptor)?.functionKind == Function) { + val synthesized = if ((invoke.getContainingDeclaration() as? FunctionClassDescriptor)?.functionKind in BUILTIN_FUNCTIONS) { createSynthesizedFunctionWithFirstParameterAsReceiver(invoke) } else { @@ -59,40 +60,15 @@ fun createSynthesizedInvokes(functions: Collection): Collect return result } -private fun createSynthesizedFunctionWithFirstParameterAsReceiver(descriptor: FunctionDescriptor): FunctionDescriptor { - val result = SimpleFunctionDescriptorImpl.create( - descriptor.containingDeclaration, - descriptor.annotations, - descriptor.name, - CallableMemberDescriptor.Kind.SYNTHESIZED, - descriptor.source - ) - - val original = descriptor.original - result.initialize( - original.valueParameters.first().type, - original.dispatchReceiverParameter, - original.typeParameters, - original.valueParameters.drop(1).map { p -> - ValueParameterDescriptorImpl( - result, null, p.index - 1, p.annotations, Name.identifier("p${p.index + 1}"), p.type, - p.declaresDefaultValue(), p.isCrossinline, p.isNoinline, p.varargElementType, p.source - ) - }, - original.returnType, - original.modality, - original.visibility - ) - result.isOperator = original.isOperator - result.isInfix = original.isInfix - result.isExternal = original.isExternal - result.isInline = original.isInline - result.isTailrec = original.isTailrec - result.setHasStableParameterNames(false) - result.setHasSynthesizedParameterNames(true) - - return result -} +private fun createSynthesizedFunctionWithFirstParameterAsReceiver(descriptor: FunctionDescriptor) = + descriptor.original.newCopyBuilder().apply { + setExtensionReceiverType(descriptor.original.valueParameters.first().type) + setValueParameters( + descriptor.original.valueParameters + .drop(1) + .map { p -> p.copy(descriptor.original, Name.identifier("p${p.index + 1}"), p.index - 1) } + ) + }.build()!! fun isSynthesizedInvoke(descriptor: DeclarationDescriptor): Boolean { if (descriptor.name != OperatorNameConventions.INVOKE || descriptor !is FunctionDescriptor) return false diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/InvokeProcessors.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/InvokeProcessors.kt index 8a7d04aa780..1b86ef910a1 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/InvokeProcessors.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/tower/InvokeProcessors.kt @@ -16,7 +16,7 @@ package org.jetbrains.kotlin.resolve.calls.tower -import org.jetbrains.kotlin.builtins.isExtensionFunctionType +import org.jetbrains.kotlin.builtins.isBuiltinExtensionFunctionalType import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.name.Name @@ -147,7 +147,7 @@ private fun ImplicitScopeTower.getExtensionInvokeCandidateDescriptor( extensionFunctionReceiver: ReceiverValueWithSmartCastInfo ): CandidateWithBoundDispatchReceiver? { val type = extensionFunctionReceiver.receiverValue.type - if (!type.isExtensionFunctionType) return null // todo: missing smart cast? + if (!type.isBuiltinExtensionFunctionalType) return null // todo: missing smart cast? val invokeDescriptor = type.memberScope.getContributedFunctions(OperatorNameConventions.INVOKE, location).single() val synthesizedInvoke = createSynthesizedInvokes(listOf(invokeDescriptor)).single() @@ -187,4 +187,4 @@ fun > createCallTowerProcessorForExplicitInvok } } -} \ No newline at end of file +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt new file mode 100644 index 00000000000..91736517737 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt @@ -0,0 +1,31 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND: JS +import kotlin.coroutines.* + +suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x -> + x.resume(v) + CoroutineIntrinsics.SUSPENDED +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +suspend fun foo(c: suspend Double.(Long, Int, String) -> String) = (1.0).c(56L, 55, "abc") + +fun box(): String { + var result = "" + var final = "" + + builder { + final = foo { l, i, s -> + result = suspendHere("$this#$l#$i#$s") + "OK" + } + } + + if (result != "1.0#56#55#abc") return "fail: $result" + + return final +} diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt new file mode 100644 index 00000000000..acd7643e6ae --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt @@ -0,0 +1,37 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND: JS +import kotlin.coroutines.* + +suspend fun suspendHere(v: String): String = CoroutineIntrinsics.suspendCoroutineOrReturn { x -> + x.resume(v) + CoroutineIntrinsics.SUSPENDED +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +suspend fun foo1(c: suspend () -> Unit) = c() +suspend fun foo2(c: suspend String.() -> Int) = "2".c() +suspend fun foo3(c: suspend (String) -> Int) = c("3") + +fun box(): String { + var result = "" + + builder { + foo1 { + result = suspendHere("begin#") + } + + val q2 = foo2 { result += suspendHere(this) + "#"; 1 } + val q3 = foo3 { result += suspendHere(it); 2 } + + if (q2 != 1) throw RuntimeException("fail q2") + if (q3 != 2) throw RuntimeException("fail q3") + } + + if (result != "begin#2#3") return "fail: $result" + + return "OK" +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionTypeCall/manyParameters.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionTypeCall/manyParameters.txt new file mode 100644 index 00000000000..9a4a15046ef --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionTypeCall/manyParameters.txt @@ -0,0 +1,20 @@ +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 +} + + +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + + +public final class ManyParametersKt { + 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 + public final static @org.jetbrains.annotations.Nullable method foo(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function5, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionTypeCall/simple.txt b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionTypeCall/simple.txt new file mode 100644 index 00000000000..dbf7096b00e --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/suspendFunctionTypeCall/simple.txt @@ -0,0 +1,22 @@ +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 +} + + +public final class EmptyContinuation { + public final static field INSTANCE: EmptyContinuation + private method (): void + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + + +public final class SimpleKt { + 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 + public final static @org.jetbrains.annotations.Nullable method foo1(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method foo2(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method foo3(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function2, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/diagnostics/tests/coroutines/inference/withParameter.kt b/compiler/testData/diagnostics/tests/coroutines/inference/withParameter.kt index f6e5f5a572c..4df5fa1a891 100644 --- a/compiler/testData/diagnostics/tests/coroutines/inference/withParameter.kt +++ b/compiler/testData/diagnostics/tests/coroutines/inference/withParameter.kt @@ -4,7 +4,7 @@ class GenericController { suspend fun yield(t: T) {} } -fun generate(p1: P1, p2: List, g: suspend GenericController.(P1, P2) -> R): Four = TODO() +fun generate(p1: P1, p2: List, g: suspend GenericController.(P1, P2) -> R): Four = TODO() val test1 = generate(1, listOf("")) { p1, p2 -> yield(p1) @@ -13,4 +13,4 @@ val test1 = generate(1, listOf("")) { p1, p2 -> } fun listOf(vararg x: X): List = TODO() -class Four \ No newline at end of file +class Four diff --git a/compiler/testData/diagnostics/tests/coroutines/inference/withUninferredParameter.kt b/compiler/testData/diagnostics/tests/coroutines/inference/withUninferredParameter.kt index 4fd04bc62aa..716c9a30460 100644 --- a/compiler/testData/diagnostics/tests/coroutines/inference/withUninferredParameter.kt +++ b/compiler/testData/diagnostics/tests/coroutines/inference/withUninferredParameter.kt @@ -4,7 +4,7 @@ class GenericController { suspend fun yield(t: T) {} } -fun generate(g: suspend GenericController.(S) -> Unit): S = TODO() +fun generate(g: suspend GenericController.(S) -> Unit): S = TODO() val test1 = generate { yield(4) diff --git a/compiler/testData/diagnostics/tests/coroutines/lambdaExpectedType.kt b/compiler/testData/diagnostics/tests/coroutines/lambdaExpectedType.kt index 14e7f8fd107..089e83bf94a 100644 --- a/compiler/testData/diagnostics/tests/coroutines/lambdaExpectedType.kt +++ b/compiler/testData/diagnostics/tests/coroutines/lambdaExpectedType.kt @@ -12,7 +12,7 @@ fun manyArgumentsBuilder( c3: suspend () -> Int ):T = null!! -fun severalParamsInLambda(c: suspend (String, Int) -> Unit) {} +fun severalParamsInLambda(c: suspend (String, Int) -> Unit) {} fun foo() { builder({ 1 }) diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt index afe468b8ff1..7ebf47f7ec1 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt @@ -1,7 +1,7 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER -fun withS(x: T, sfn: suspend (T) -> Unit) = x +fun withS(x: T, sfn: suspend (T) -> Unit) = x val test1 = withS(100) {} -fun test2(x: TT) = withS(x) {} \ No newline at end of file +fun test2(x: TT) = withS(x) {} diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt index f52af7785ab..ca95fcc3f5b 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt @@ -1,5 +1,5 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER -fun withS2(x: T1, sfn1: suspend (T1) -> T2, sfn2: suspend (T2) -> Unit): T2 = null!! +fun withS2(x: T1, sfn1: suspend (T1) -> T2, sfn2: suspend (T2) -> Unit): T2 = null!! -val test1 = withS2(100, { it.toLong().toString() }, { it.length }) \ No newline at end of file +val test1 = withS2(100, { it.toLong().toString() }, { it.length }) diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/invoke.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/invoke.kt new file mode 100644 index 00000000000..8e54142db22 --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/invoke.kt @@ -0,0 +1,10 @@ +suspend fun foo1(q: suspend () -> Unit) = q() +suspend fun foo2(x: suspend (Int) -> String) = x(1) + + +suspend fun foo3(y: suspend String.(Int) -> Double) = "".y(1) +suspend fun String.foo4(y: suspend String.(Int) -> Double) = "".y(1) + +fun noSuspend(x: suspend (Int) -> String) { + x(1) +} diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/invoke.txt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/invoke.txt new file mode 100644 index 00000000000..bbef1400386 --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/invoke.txt @@ -0,0 +1,7 @@ +package + +public suspend fun foo1(/*0*/ q: suspend () -> kotlin.Unit): kotlin.Unit +public suspend fun foo2(/*0*/ x: suspend (kotlin.Int) -> kotlin.String): kotlin.String +public suspend fun foo3(/*0*/ y: suspend kotlin.String.(kotlin.Int) -> kotlin.Double): kotlin.Double +public fun noSuspend(/*0*/ x: suspend (kotlin.Int) -> kotlin.String): kotlin.Unit +public suspend fun kotlin.String.foo4(/*0*/ y: suspend kotlin.String.(kotlin.Int) -> kotlin.Double): kotlin.Double diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/lambdaInValInitializer.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/lambdaInValInitializer.kt index d1342e3b041..d54b82ca84d 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/lambdaInValInitializer.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/lambdaInValInitializer.kt @@ -2,5 +2,5 @@ typealias SuspendFn = suspend () -> Unit val test1: suspend () -> Unit = {} val test2: suspend Any.() -> Unit = {} -val test3: suspend Any.(Int) -> Int = { k: Int -> k + 1 } -val test4: SuspendFn = {} \ No newline at end of file +val test3: suspend Any.(Int) -> Int = { k: Int -> k + 1 } +val test4: SuspendFn = {} diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.kt index 332b15577b7..e7b453b6567 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.kt @@ -12,7 +12,7 @@ typealias Test5 = List Unit> typealias Test6 = suspend List<() -> Unit> typealias Test7 = suspend SAM typealias Test8 = suspend SuspendFunction0 -typealias Test9 = suspend (() -> Unit) -> Unit -typealias Test10 = suspend (suspend () -> Unit) -> Unit +typealias Test9 = suspend (() -> Unit) -> Unit +typealias Test10 = suspend (suspend () -> Unit) -> Unit typealias Test11 = suspend () -> (suspend () -> Unit) -typealias Test12 = suspend (suspend (() -> Unit)) -> Unit \ No newline at end of file +typealias Test12 = suspend (suspend (() -> Unit)) -> Unit diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt index 1299adfcac9..a60a0040aa4 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt @@ -1,2 +1,2 @@ -fun test1(sfn: suspend () -> Unit) = sfn() -fun test2(sfn: suspend () -> Unit) = sfn.invoke() \ No newline at end of file +fun test1(sfn: suspend () -> Unit) = sfn() +fun test2(sfn: suspend () -> Unit) = sfn.invoke() diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.txt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.txt index 123d33e963f..d72f95e20ce 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.txt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noInvokeForSuspendFunction.txt @@ -1,4 +1,4 @@ package -public fun test1(/*0*/ sfn: suspend () -> kotlin.Unit): [ERROR : Error function type] -public fun test2(/*0*/ sfn: suspend () -> kotlin.Unit): [ERROR : Error function type] +public fun test1(/*0*/ sfn: suspend () -> kotlin.Unit): kotlin.Unit +public fun test2(/*0*/ sfn: suspend () -> kotlin.Unit): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noValueParameters.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noValueParameters.kt index f4a200c1be4..bddc9b15182 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noValueParameters.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/noValueParameters.kt @@ -1,4 +1,4 @@ -typealias Test1 = suspend (Int) -> Unit -typealias Test2 = suspend Int.(Int) -> Unit -typealias Test3 = List(Int) -> Unit> -typealias Test4 = ListInt.(Int) -> Unit> \ No newline at end of file +typealias Test1 = suspend (Int) -> Unit +typealias Test2 = suspend Int.(Int) -> Unit +typealias Test3 = List Unit> +typealias Test4 = List Unit> 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 300fce456c3..28eb1bada6a 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 @@ -4973,6 +4973,27 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionTypeCall extends AbstractIrBlackBoxCodegenTest { + public void testAllFilesPresentInSuspendFunctionTypeCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("manyParameters.kt") + public void testManyParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/unitTypeReturn") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index aca8b8bd4ff..33a9004fa1c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -4506,6 +4506,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("invoke.kt") + public void testInvoke() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/invoke.kt"); + doTest(fileName); + } + @TestMetadata("lambdaInOverriddenValInitializer.kt") public void testLambdaInOverriddenValInitializer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/lambdaInOverriddenValInitializer.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index bd832db0c74..64ade5a7f69 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4973,6 +4973,27 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionTypeCall extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInSuspendFunctionTypeCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("manyParameters.kt") + public void testManyParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/unitTypeReturn") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 664bcf3eca7..d364fb300db 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -4973,6 +4973,27 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionTypeCall extends AbstractLightAnalysisModeCodegenTest { + public void testAllFilesPresentInSuspendFunctionTypeCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); + } + + @TestMetadata("manyParameters.kt") + public void testManyParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/codegen/box/coroutines/unitTypeReturn") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassScope.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassScope.kt index 7d255bbca0b..670fc899377 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassScope.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassScope.kt @@ -25,8 +25,9 @@ class FunctionClassScope( containingClass: FunctionClassDescriptor ) : GivenFunctionsMemberScope(storageManager, containingClass) { override fun computeDeclaredFunctions(): List = - if ((containingClass as FunctionClassDescriptor).functionKind == FunctionClassDescriptor.Kind.Function) { - listOf(FunctionInvokeDescriptor.create(containingClass)) + when ((containingClass as FunctionClassDescriptor).functionKind) { + FunctionClassDescriptor.Kind.Function -> listOf(FunctionInvokeDescriptor.create(containingClass, isSuspend = false)) + FunctionClassDescriptor.Kind.SuspendFunction -> listOf(FunctionInvokeDescriptor.create(containingClass, isSuspend = true)) + else -> emptyList() } - else emptyList() } diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionInvokeDescriptor.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionInvokeDescriptor.kt index e69d07b6e89..ca490c18fd7 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionInvokeDescriptor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionInvokeDescriptor.kt @@ -30,7 +30,8 @@ import org.jetbrains.kotlin.util.OperatorNameConventions class FunctionInvokeDescriptor private constructor( container: DeclarationDescriptor, original: FunctionInvokeDescriptor?, - callableKind: CallableMemberDescriptor.Kind + callableKind: CallableMemberDescriptor.Kind, + isSuspend: Boolean ) : SimpleFunctionDescriptorImpl( container, original, @@ -41,6 +42,7 @@ class FunctionInvokeDescriptor private constructor( ) { init { this.isOperator = true + this.isSuspend = isSuspend this.setHasStableParameterNames(false) } @@ -59,7 +61,7 @@ class FunctionInvokeDescriptor private constructor( annotations: Annotations, source: SourceElement ): FunctionDescriptorImpl { - return FunctionInvokeDescriptor(newOwner, original as FunctionInvokeDescriptor?, kind) + return FunctionInvokeDescriptor(newOwner, original as FunctionInvokeDescriptor?, kind, isSuspend) } override fun isExternal(): Boolean = false @@ -94,10 +96,10 @@ class FunctionInvokeDescriptor private constructor( } companion object Factory { - fun create(functionClass: FunctionClassDescriptor): FunctionInvokeDescriptor { + fun create(functionClass: FunctionClassDescriptor, isSuspend: Boolean): FunctionInvokeDescriptor { val typeParameters = functionClass.declaredTypeParameters - val result = FunctionInvokeDescriptor(functionClass, null, CallableMemberDescriptor.Kind.DECLARATION) + val result = FunctionInvokeDescriptor(functionClass, null, CallableMemberDescriptor.Kind.DECLARATION, isSuspend) result.initialize( null, functionClass.thisAsReceiverParameter, 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 d69677c0a58..c3e7c77e087 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 @@ -5760,6 +5760,39 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } + @TestMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class SuspendFunctionTypeCall extends AbstractJsCodegenBoxTest { + public void testAllFilesPresentInSuspendFunctionTypeCall() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); + } + + @TestMetadata("manyParameters.kt") + public void testManyParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/manyParameters.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("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/suspendFunctionTypeCall/simple.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("compiler/testData/codegen/box/coroutines/unitTypeReturn") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)