From 6d73e1379865cc2c2d1b1ce29192b1754c5ca98f Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 14 Dec 2016 18:00:52 +0300 Subject: [PATCH] Generic type arguments inference for lambdas as suspend functions. --- .../resolve/FunctionDescriptorResolver.kt | 7 +--- .../kotlin/resolve/calls/CallResolverUtil.kt | 7 ++-- .../resolve/calls/GenericCandidateResolver.kt | 3 +- .../inference/ConstraintSystemBuilderImpl.kt | 4 +- .../functionVsSuspendFunction.kt | 2 +- .../suspendFunctionType/inference1.kt | 7 ++++ .../suspendFunctionType/inference1.txt | 5 +++ .../suspendFunctionType/inference2.kt | 5 +++ .../suspendFunctionType/inference2.txt | 4 ++ .../kotlin/builtins/functionTypes.kt | 37 +++++++++++-------- 10 files changed, 55 insertions(+), 26 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt create mode 100644 compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.txt create mode 100644 compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt create mode 100644 compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt index f0e311a1f08..fcb5db08ba2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/FunctionDescriptorResolver.kt @@ -17,10 +17,7 @@ package org.jetbrains.kotlin.resolve import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType -import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType -import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.builtins.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl @@ -239,7 +236,7 @@ class FunctionDescriptorResolver( return replaceAnnotations(AnnotationsImpl(annotations.filter { it != parameterNameAnnotation })) } - private fun KotlinType.functionTypeExpected() = !TypeUtils.noExpectedType(this) && isFunctionType + private fun KotlinType.functionTypeExpected() = !TypeUtils.noExpectedType(this) && isBuiltinFunctionalType private fun KotlinType.getReceiverType(): KotlinType? = if (functionTypeExpected()) this.getReceiverTypeFromFunctionType() else null diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt index fbd43c51522..05e39d45841 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/CallResolverUtil.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.callResolverUtil import com.google.common.collect.Lists import com.intellij.util.containers.ContainerUtil import org.jetbrains.kotlin.builtins.ReflectionTypes +import org.jetbrains.kotlin.builtins.isSuspendFunctionType import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptorImpl import org.jetbrains.kotlin.lexer.KtToken @@ -50,19 +51,19 @@ enum class ResolveArgumentsMode { fun hasUnknownFunctionParameter(type: KotlinType): Boolean { - assert(ReflectionTypes.isCallableType(type)) { "type $type is not a function or property" } + assert(ReflectionTypes.isCallableType(type) || type.isSuspendFunctionType) { "type $type is not a function or property" } return getParameterArgumentsOfCallableType(type).any { it.type.contains { TypeUtils.isDontCarePlaceholder(it) } || ErrorUtils.containsUninferredParameter(it.type) } } fun hasUnknownReturnType(type: KotlinType): Boolean { - assert(ReflectionTypes.isCallableType(type)) { "type $type is not a function or property" } + assert(ReflectionTypes.isCallableType(type) || type.isSuspendFunctionType) { "type $type is not a function or property" } return ErrorUtils.containsErrorType(getReturnTypeForCallable(type)) } fun replaceReturnTypeForCallable(type: KotlinType, given: KotlinType): KotlinType { - assert(ReflectionTypes.isCallableType(type)) { "type $type is not a function or property" } + assert(ReflectionTypes.isCallableType(type) || type.isSuspendFunctionType) { "type $type is not a function or property" } val newArguments = Lists.newArrayList() newArguments.addAll(getParameterArgumentsOfCallableType(type)) newArguments.add(TypeProjectionImpl(Variance.INVARIANT, given)) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt index 1cefc16d8f7..d8b4a4cfaf9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve.calls import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.ReflectionTypes +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype import org.jetbrains.kotlin.builtins.isFunctionTypeOrSubtype import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor @@ -308,7 +309,7 @@ class GenericCandidateResolver(private val argumentTypeResolver: ArgumentTypeRes if (expectedType == null || TypeUtils.isDontCarePlaceholder(expectedType)) { expectedType = argumentTypeResolver.getShapeTypeOfFunctionLiteral(functionLiteral, context.scope, context.trace, false) } - if (expectedType == null || !expectedType.isFunctionTypeOrSubtype || hasUnknownFunctionParameter(expectedType)) { + if (expectedType == null || !expectedType.isBuiltinFunctionalTypeOrSubtype || hasUnknownFunctionParameter(expectedType)) { return } val dataFlowInfoForArguments = context.candidateCall.dataFlowInfoForArguments diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt index 36790da8468..c62a3414dd1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemBuilderImpl.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.resolve.calls.inference import org.jetbrains.kotlin.builtins.createFunctionType import org.jetbrains.kotlin.builtins.isExtensionFunctionType +import org.jetbrains.kotlin.builtins.isSuspendFunctionType import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilderImpl.ConstraintKind.EQUAL @@ -448,5 +449,6 @@ internal fun createTypeForFunctionPlaceholder( functionPlaceholderTypeConstructor.argumentTypes } val receiverType = if (isExtension) DONT_CARE else null - return createFunctionType(functionPlaceholder.builtIns, Annotations.EMPTY, receiverType, newArgumentTypes, null, DONT_CARE) + return createFunctionType(functionPlaceholder.builtIns, Annotations.EMPTY, receiverType, newArgumentTypes, null, DONT_CARE, + suspendFunction = expectedType.isSuspendFunctionType) } diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/functionVsSuspendFunction.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/functionVsSuspendFunction.kt index 88dd5fede53..002aa48783f 100644 --- a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/functionVsSuspendFunction.kt +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/functionVsSuspendFunction.kt @@ -14,4 +14,4 @@ fun test4(): suspend () -> Unit = useSuspendFn {} fun test5(sfn: suspend () -> Unit) = ambiguous(sfn) fun test6(fn: () -> Unit) = ambiguous(fn) -fun test7(): () -> Unit = ambiguous {} \ No newline at end of file +fun test7(): () -> Unit = ambiguous {} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt new file mode 100644 index 00000000000..8ba65a2825b --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.kt @@ -0,0 +1,7 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +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 diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.txt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.txt new file mode 100644 index 00000000000..a7aff3d6c2e --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference1.txt @@ -0,0 +1,5 @@ +package + +public val test1: kotlin.Int +public fun test2(/*0*/ x: TT): TT +public fun withS(/*0*/ x: T, /*1*/ sfn: suspend (T) -> kotlin.Unit): T diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt new file mode 100644 index 00000000000..2133e72a5d3 --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.kt @@ -0,0 +1,5 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun withS2(x: T1, sfn1: suspend (T1) -> T2, sfn2: suspend (T2) -> Unit): T2 = null!! + +val test1 = withS2(100, { "" }, { }) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.txt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.txt new file mode 100644 index 00000000000..121c3bce89f --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/inference2.txt @@ -0,0 +1,4 @@ +package + +public val test1: kotlin.String +public fun withS2(/*0*/ x: T1, /*1*/ sfn1: suspend (T1) -> T2, /*2*/ sfn2: suspend (T2) -> kotlin.Unit): T2 diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt index 3a2220ba727..241197bf88d 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt @@ -41,24 +41,31 @@ import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.check import java.util.* -val KotlinType.isFunctionTypeOrSubtype: Boolean - get() = isFunctionType || DFS.dfsFromNode( - this, - DFS.Neighbors { it.constructor.supertypes }, - DFS.VisitedWithSet(), - object : DFS.AbstractNodeHandler() { - private var result = false +private fun KotlinType.isTypeOrSubtypeOf(predicate: (KotlinType) -> Boolean): Boolean = + predicate(this) || + DFS.dfsFromNode( + this, + DFS.Neighbors { it.constructor.supertypes }, + DFS.VisitedWithSet(), + object : DFS.AbstractNodeHandler() { + private var result = false - override fun beforeChildren(current: KotlinType): Boolean { - if (current.isFunctionType) { - result = true + override fun beforeChildren(current: KotlinType): Boolean { + if (predicate(current)) { + result = true + } + return !result } - return !result - } - override fun result() = result - } - ) + override fun result() = result + } + ) + +val KotlinType.isFunctionTypeOrSubtype: Boolean + get() = isTypeOrSubtypeOf { it.isFunctionType } + +val KotlinType.isBuiltinFunctionalTypeOrSubtype: Boolean + get() = isTypeOrSubtypeOf { it.isBuiltinFunctionalType } val KotlinType.isFunctionType: Boolean get() = constructor.declarationDescriptor?.getFunctionalClassKind() == FunctionClassDescriptor.Kind.Function