diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt index a7b4108b399..8e69915f77f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TypeResolver.kt @@ -134,7 +134,7 @@ class TypeResolver( val typeElement = typeReference.typeElement - val type = resolveTypeElement(c, annotations, typeElement) + val type = resolveTypeElement(c, annotations, typeReference.modifierList, typeElement) c.trace.recordScope(c.scope, typeReference) if (!type.isBare) { @@ -175,8 +175,14 @@ class TypeResolver( } } - private fun resolveTypeElement(c: TypeResolutionContext, annotations: Annotations, typeElement: KtTypeElement?): PossiblyBareType { + private fun resolveTypeElement(c: TypeResolutionContext, annotations: Annotations, modifiers: KtModifierList?, typeElement: KtTypeElement?): PossiblyBareType { var result: PossiblyBareType? = null + + val hasSuspendModifier = modifiers?.hasModifier(KtTokens.SUSPEND_KEYWORD) ?: false + if (hasSuspendModifier && typeElement !is KtFunctionType) { + c.trace.report(Errors.WRONG_MODIFIER_TARGET.on(modifiers!!.getModifier(KtTokens.SUSPEND_KEYWORD)!!, KtTokens.SUSPEND_KEYWORD, "non-functional type")) + } + typeElement?.accept(object : KtVisitorVoid() { override fun visitUserType(type: KtUserType) { val qualifierResolutionResult = resolveDescriptorForType(c.scope, type, c.trace, c.isDebuggerContext) @@ -198,7 +204,7 @@ class TypeResolver( override fun visitNullableType(nullableType: KtNullableType) { val innerType = nullableType.getInnerType() - val baseType = resolveTypeElement(c, annotations, innerType) + val baseType = resolveTypeElement(c, annotations, modifiers, innerType) if (baseType.isNullable || innerType is KtNullableType || innerType is KtDynamicType) { c.trace.report(REDUNDANT_NULLABLE.on(nullableType)) } @@ -219,7 +225,8 @@ class TypeResolver( moduleDescriptor.builtIns, annotations, receiverType, parameterDescriptors.map { it.type }, parameterDescriptors.map { it.name }, - returnType + returnType, + suspendFunction = hasSuspendModifier )) } diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.kt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.kt new file mode 100644 index 00000000000..c89db91ceb8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.kt @@ -0,0 +1,14 @@ +typealias Action = () -> Unit + +interface SAM { + fun run() +} + +typealias Test1 = suspend () -> Unit +typealias Test2 = suspend Int.(String) -> Unit +typealias Test3 = suspend Function0 +typealias Test4 = suspend Action +typealias Test5 = List Unit> +typealias Test6 = suspend List<() -> Unit> +typealias Test7 = suspend SAM +typealias Test8 = SuspendFunction0 \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.txt b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.txt new file mode 100644 index 00000000000..6fe2357fe51 --- /dev/null +++ b/compiler/testData/diagnostics/tests/coroutines/suspendFunctionType/modifierApplicability.txt @@ -0,0 +1,17 @@ +package + +public interface SAM { + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract fun run(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +public typealias Action = () -> kotlin.Unit +public typealias Test1 = suspend () -> kotlin.Unit +public typealias Test2 = suspend kotlin.Int.(kotlin.String) -> kotlin.Unit +public typealias Test3 = () -> kotlin.Unit +public typealias Test4 = Action +public typealias Test5 = kotlin.collections.List kotlin.Unit> +public typealias Test6 = kotlin.collections.List<() -> kotlin.Unit> +public typealias Test7 = SAM +public typealias Test8 = suspend () -> kotlin.Unit diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java index 650456d8970..8e50d1a5592 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/KotlinBuiltIns.java @@ -523,6 +523,11 @@ public abstract class KotlinBuiltIns { return "Function" + parameterCount; } + @NotNull + public static String getSuspendFunctionName(int parameterCount) { + return "SuspendFunction" + parameterCount; + } + @NotNull public static ClassId getFunctionClassId(int parameterCount) { return new ClassId(BUILT_INS_PACKAGE_FQ_NAME, Name.identifier(getFunctionName(parameterCount))); @@ -533,6 +538,11 @@ public abstract class KotlinBuiltIns { return getBuiltInClassByName(getFunctionName(parameterCount)); } + @NotNull + public ClassDescriptor getSuspendFunction(int parameterCount) { + return getBuiltInClassByName(getSuspendFunctionName(parameterCount)); + } + @NotNull public ClassDescriptor getThrowable() { return getBuiltInClassByName("Throwable"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt index 6874b306565..df3b922580c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functionTypes.kt @@ -18,6 +18,9 @@ package org.jetbrains.kotlin.builtins import org.jetbrains.kotlin.builtins.functions.BuiltInFictitiousFunctionClassFactory import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl @@ -57,16 +60,23 @@ val KotlinType.isFunctionTypeOrSubtype: Boolean ) val KotlinType.isFunctionType: Boolean + get() = constructor.declarationDescriptor?.getFunctionalClassKind() == FunctionClassDescriptor.Kind.Function + +val KotlinType.isSuspendFunctionType: Boolean + get() = constructor.declarationDescriptor?.getFunctionalClassKind() == FunctionClassDescriptor.Kind.SuspendFunction + +val KotlinType.isBuiltinFunctionalType: Boolean get() { - val descriptor = constructor.declarationDescriptor - return descriptor != null && isNumberedFunctionClassFqName(descriptor.fqNameUnsafe) + val kind = constructor.declarationDescriptor?.getFunctionalClassKind() + return kind == FunctionClassDescriptor.Kind.Function || + kind == FunctionClassDescriptor.Kind.SuspendFunction } val KotlinType.isNonExtensionFunctionType: Boolean - get() = isFunctionType && !isTypeAnnotatedWithExtensionFunctionType + get() = isBuiltinFunctionalType && !isTypeAnnotatedWithExtensionFunctionType val KotlinType.isExtensionFunctionType: Boolean - get() = isFunctionType && isTypeAnnotatedWithExtensionFunctionType + get() = isBuiltinFunctionalType && isTypeAnnotatedWithExtensionFunctionType private val KotlinType.isTypeAnnotatedWithExtensionFunctionType: Boolean get() = annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType) != null @@ -85,18 +95,29 @@ fun isNumberedFunctionClassFqName(fqName: FqNameUnsafe): Boolean { return BuiltInFictitiousFunctionClassFactory.isFunctionClassName(shortName, KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME) } +fun DeclarationDescriptor.getFunctionalClassKind(): FunctionClassDescriptor.Kind? { + if (this !is ClassDescriptor) return null + + val fqNameUnsafe = this.fqNameUnsafe + if (!fqNameUnsafe.isSafe || fqNameUnsafe.isRoot) return null + val fqName = fqNameUnsafe.toSafe() + + return BuiltInFictitiousFunctionClassFactory.getFunctionalClassKind(fqName.shortName().asString(), fqName.parent()) +} + + fun KotlinType.getReceiverTypeFromFunctionType(): KotlinType? { - assert(isFunctionType) { "Not a function type: ${this}" } + assert(isBuiltinFunctionalType) { "Not a function type: ${this}" } return if (isTypeAnnotatedWithExtensionFunctionType) arguments.first().type else null } fun KotlinType.getReturnTypeFromFunctionType(): KotlinType { - assert(isFunctionType) { "Not a function type: ${this}" } + assert(isBuiltinFunctionalType) { "Not a function type: ${this}" } return arguments.last().type } fun KotlinType.getValueParameterTypesFromFunctionType(): List { - assert(isFunctionType) { "Not a function type: ${this}" } + assert(isBuiltinFunctionalType) { "Not a function type: ${this}" } val arguments = arguments val first = if (isExtensionFunctionType) 1 else 0 val last = arguments.size - 1 @@ -147,17 +168,20 @@ fun getFunctionTypeArgumentProjections( return arguments } +@JvmOverloads fun createFunctionType( builtIns: KotlinBuiltIns, annotations: Annotations, receiverType: KotlinType?, parameterTypes: List, parameterNames: List?, - returnType: KotlinType + returnType: KotlinType, + suspendFunction: Boolean = false ): SimpleType { val arguments = getFunctionTypeArgumentProjections(receiverType, parameterTypes, parameterNames, returnType, builtIns) val size = parameterTypes.size - val classDescriptor = builtIns.getFunction(if (receiverType == null) size else size + 1) + val parameterCount = if (receiverType == null) size else size + 1 + val classDescriptor = if (suspendFunction) builtIns.getSuspendFunction(parameterCount) else builtIns.getFunction(parameterCount) val typeAnnotations = if (receiverType == null || annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.extensionFunctionType) != null) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/BuiltInFictitiousFunctionClassFactory.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/BuiltInFictitiousFunctionClassFactory.kt index d8bff6048c0..aca12e2d679 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/BuiltInFictitiousFunctionClassFactory.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/BuiltInFictitiousFunctionClassFactory.kt @@ -38,10 +38,9 @@ class BuiltInFictitiousFunctionClassFactory( companion object { private fun parseClassName(className: String, packageFqName: FqName): KindWithArity? { - val kind = FunctionClassDescriptor.Kind.byPackage(packageFqName) ?: return null + val kind = FunctionClassDescriptor.Kind.byClassNamePrefix(packageFqName, className) ?: return null val prefix = kind.classNamePrefix - if (!className.startsWith(prefix)) return null val arity = toInt(className.substring(prefix.length)) ?: return null @@ -49,8 +48,14 @@ class BuiltInFictitiousFunctionClassFactory( return KindWithArity(kind, arity) } + @JvmStatic fun getFunctionalClassKind(className: String, packageFqName: FqName) = + parseClassName(className, packageFqName)?.kind + @JvmStatic fun isFunctionClassName(className: String, packageFqName: FqName) = - parseClassName(className, packageFqName) != null + getFunctionalClassKind(className, packageFqName) == Kind.Function + + @JvmStatic fun isSuspendFunctionClassName(className: String, packageFqName: FqName) = + getFunctionalClassKind(className, packageFqName) == Kind.SuspendFunction private fun toInt(s: String): Int? { if (s.isEmpty()) return null @@ -67,7 +72,7 @@ class BuiltInFictitiousFunctionClassFactory( override fun shouldCreateClass(packageFqName: FqName, name: Name): Boolean { val string = name.asString() - return (string.startsWith("Function") || string.startsWith("KFunction")) // an optimization + return (string.startsWith("Function") || string.startsWith("KFunction") || string.startsWith("SuspendFunction")) // an optimization && parseClassName(string, packageFqName) != null } diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassDescriptor.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassDescriptor.kt index ea8b4079cfa..340481c0ccd 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassDescriptor.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/functions/FunctionClassDescriptor.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.types.* @@ -49,6 +50,7 @@ class FunctionClassDescriptor( enum class Kind(val packageFqName: FqName, val classNamePrefix: String) { Function(BUILT_INS_PACKAGE_FQ_NAME, "Function"), + SuspendFunction(BUILT_INS_PACKAGE_FQ_NAME, "SuspendFunction"), KFunction(KOTLIN_REFLECT_FQ_NAME, "KFunction"); fun numberedClassName(arity: Int) = Name.identifier("$classNamePrefix$arity") @@ -59,6 +61,9 @@ class FunctionClassDescriptor( KOTLIN_REFLECT_FQ_NAME -> KFunction else -> null } + + fun byClassNamePrefix(packageFqName: FqName, className: String) = + Kind.values().firstOrNull { it.packageFqName == packageFqName && className.startsWith(it.classNamePrefix) } } } @@ -128,8 +133,15 @@ class FunctionClassDescriptor( result.add(KotlinTypeFactory.simpleNotNullType(Annotations.EMPTY, descriptor, arguments)) } - // Add unnumbered base class, e.g. Function for Function{n}, KFunction for KFunction{n} - add(containingDeclaration, Name.identifier(functionKind.classNamePrefix)) + + if (functionKind == Kind.SuspendFunction) { + // SuspendFunction$N<...> <: Any + result.add(containingDeclaration.builtIns.anyType) + } + else { + // Add unnumbered base class, e.g. Function for Function{n}, KFunction for KFunction{n} + add(containingDeclaration, Name.identifier(functionKind.classNamePrefix)) + } // For KFunction{n}, add corresponding numbered Function{n} class, e.g. Function2 for KFunction2 if (functionKind == Kind.KFunction) { diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt index 4e27dd39ae2..38fb7bab41a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt @@ -196,7 +196,7 @@ internal class DescriptorRendererImpl( } private fun shouldRenderAsPrettyFunctionType(type: KotlinType): Boolean { - return type.isFunctionType && type.arguments.none { it.isStarProjection } + return type.isBuiltinFunctionalType && type.arguments.none { it.isStarProjection } } override fun renderFlexibleType(lowerRendered: String, upperRendered: String, builtIns: KotlinBuiltIns): String { @@ -322,6 +322,10 @@ internal class DescriptorRendererImpl( if (needParenthesis) append("(") + if (type.isSuspendFunctionType) { + append("suspend ") // anything special about modifier? + } + if (receiverType != null) { val surroundReceiver = shouldRenderAsPrettyFunctionType(receiverType) && !receiverType.isMarkedNullable if (surroundReceiver) {