From e49cdf0ca2e490936fbbfd8bf9f789bcd0f4332e Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Mon, 10 Aug 2020 18:49:55 +0300 Subject: [PATCH] Prohibit using `suspend` functions as SAM in `fun` interfaces #KT-40978 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../FunInterfaceDeclarationChecker.kt | 18 ++++++++++++++-- .../suspendFunInterfaceConversionCodegen.kt | 1 + .../funInterfaceDeclarationCheck.kt | 2 +- .../suspendFunInterfaceConversion.fir.kt | 14 +++++++++++++ .../suspendFunInterfaceConversion.kt | 3 +-- ...FunSuspendConversionForSimpleExpression.kt | 2 +- .../suspendAndFunConversionInDisabledMode.kt | 2 +- .../suspendConversionWithFunInterfaces.fir.kt | 21 +++++++++++++++++++ .../suspendConversionWithFunInterfaces.kt | 3 +-- .../chainedFunSuspendUnitConversion.kt | 2 +- 12 files changed, 60 insertions(+), 10 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.fir.kt create mode 100644 compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.fir.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index ec9e068678c..d86ca1ec5ab 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -376,6 +376,7 @@ public interface Errors { DiagnosticFactory0 FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 FUN_INTERFACE_CONSTRUCTOR_REFERENCE = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 FUN_INTERFACE_WITH_SUSPEND_FUNCTION = DiagnosticFactory0.create(ERROR); // Secondary constructors diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index a9f79469e77..c783f9fe505 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -718,6 +718,7 @@ public class DefaultErrorMessages { MAP.put(FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS, "Single abstract member cannot declare type parameters"); MAP.put(FUN_INTERFACE_ABSTRACT_METHOD_WITH_DEFAULT_VALUE, "Single abstract member cannot declare default values"); MAP.put(FUN_INTERFACE_CONSTRUCTOR_REFERENCE, "Functional interface constructor references are prohibited"); + MAP.put(FUN_INTERFACE_WITH_SUSPEND_FUNCTION, "'suspend' modifier is not allowed on a single abstract member"); MAP.put(VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, "Variance annotations are only allowed for type parameters of classes and interfaces"); MAP.put(BOUND_ON_TYPE_ALIAS_PARAMETER_NOT_ALLOWED, "Bounds are not allowed on type alias parameters"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/FunInterfaceDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/FunInterfaceDeclarationChecker.kt index d584dfea9ed..b6b4648f2d8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/FunInterfaceDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/FunInterfaceDeclarationChecker.kt @@ -6,9 +6,16 @@ package org.jetbrains.kotlin.resolve.checkers import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.resolve.calls.components.hasDefaultValue import org.jetbrains.kotlin.resolve.sam.getAbstractMembers import org.jetbrains.kotlin.resolve.source.getPsi @@ -52,6 +59,13 @@ class FunInterfaceDeclarationChecker : DeclarationChecker { context: DeclarationCheckerContext, ) { val ktFunction = abstractMember.source.getPsi() as? KtNamedFunction + + if (abstractMember.isSuspend) { + val reportOn = ktFunction?.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: funInterfaceKeyword + context.trace.report(Errors.FUN_INTERFACE_WITH_SUSPEND_FUNCTION.on(reportOn)) + return + } + if (abstractMember.typeParameters.isNotEmpty()) { val reportOn = ktFunction?.typeParameterList ?: ktFunction?.funKeyword ?: funInterfaceKeyword context.trace.report(Errors.FUN_INTERFACE_ABSTRACT_METHOD_WITH_TYPE_PARAMETERS.on(reportOn)) diff --git a/compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt b/compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt index 04c6bae74a0..1d57b759264 100644 --- a/compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt +++ b/compiler/testData/codegen/box/funInterface/suspendFunInterfaceConversionCodegen.kt @@ -6,6 +6,7 @@ import helpers.* import kotlin.coroutines.* fun interface SuspendRunnable { + @Suppress("FUN_INTERFACE_WITH_SUSPEND_FUNCTION") suspend fun invoke() } diff --git a/compiler/testData/diagnostics/tests/funInterface/funInterfaceDeclarationCheck.kt b/compiler/testData/diagnostics/tests/funInterface/funInterfaceDeclarationCheck.kt index 2fef1295262..7df2da4d4a0 100644 --- a/compiler/testData/diagnostics/tests/funInterface/funInterfaceDeclarationCheck.kt +++ b/compiler/testData/diagnostics/tests/funInterface/funInterfaceDeclarationCheck.kt @@ -71,7 +71,7 @@ interface BaseWithGeneric { fun interface GoodExtensionGeneric : GoodGeneric fun interface GoodSuspend { - suspend fun invoke() + suspend fun invoke() } class WithNestedFun { diff --git a/compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.fir.kt b/compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.fir.kt new file mode 100644 index 00000000000..8c60a7b0cfd --- /dev/null +++ b/compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.fir.kt @@ -0,0 +1,14 @@ +// !LANGUAGE: +NewInference +SamConversionForKotlinFunctions +SamConversionPerArgument +FunctionalInterfaceConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER -NOTHING_TO_INLINE + +fun interface SuspendRunnable { + suspend fun invoke() +} + +fun run(r: SuspendRunnable) {} + +suspend fun bar() {} + +fun test() { + run(::bar) +} diff --git a/compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.kt b/compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.kt index 0e82d898a1f..8faa36cc9db 100644 --- a/compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.kt +++ b/compiler/testData/diagnostics/tests/funInterface/suspendFunInterfaceConversion.kt @@ -1,9 +1,8 @@ -// FIR_IDENTICAL // !LANGUAGE: +NewInference +SamConversionForKotlinFunctions +SamConversionPerArgument +FunctionalInterfaceConversion // !DIAGNOSTICS: -UNUSED_PARAMETER -NOTHING_TO_INLINE fun interface SuspendRunnable { - suspend fun invoke() + suspend fun invoke() } fun run(r: SuspendRunnable) {} diff --git a/compiler/testData/diagnostics/tests/suspendConversion/chainedFunSuspendConversionForSimpleExpression.kt b/compiler/testData/diagnostics/tests/suspendConversion/chainedFunSuspendConversionForSimpleExpression.kt index e26d3fcaf19..17f0bf5e12f 100644 --- a/compiler/testData/diagnostics/tests/suspendConversion/chainedFunSuspendConversionForSimpleExpression.kt +++ b/compiler/testData/diagnostics/tests/suspendConversion/chainedFunSuspendConversionForSimpleExpression.kt @@ -2,7 +2,7 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER fun interface SuspendRunnable { - suspend fun invoke() + suspend fun invoke() } fun foo(s: SuspendRunnable) {} diff --git a/compiler/testData/diagnostics/tests/suspendConversion/suspendAndFunConversionInDisabledMode.kt b/compiler/testData/diagnostics/tests/suspendConversion/suspendAndFunConversionInDisabledMode.kt index 3ae5855bfa9..1437e84447d 100644 --- a/compiler/testData/diagnostics/tests/suspendConversion/suspendAndFunConversionInDisabledMode.kt +++ b/compiler/testData/diagnostics/tests/suspendConversion/suspendAndFunConversionInDisabledMode.kt @@ -2,7 +2,7 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION fun interface SuspendRunnable { - suspend fun run() + suspend fun run() } object Test1 { diff --git a/compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.fir.kt b/compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.fir.kt new file mode 100644 index 00000000000..9737580400a --- /dev/null +++ b/compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.fir.kt @@ -0,0 +1,21 @@ +// !LANGUAGE: +SuspendConversion +// !DIAGNOSTICS: -UNUSED_PARAMETER + +fun interface SuspendRunnable { + suspend fun invoke() +} + +fun foo1(s: SuspendRunnable) {} +fun bar1() {} + +fun bar2(s: String = ""): Int = 0 + +fun bar3() {} +suspend fun bar3(s: String = ""): Int = 0 + +fun test() { + foo1(::bar1) + foo1(::bar2) + + foo1(::bar3) // Should be ambiguity +} diff --git a/compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.kt b/compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.kt index fbcbf56ace7..d36d390e226 100644 --- a/compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.kt +++ b/compiler/testData/diagnostics/tests/suspendConversion/suspendConversionWithFunInterfaces.kt @@ -1,9 +1,8 @@ -// FIR_IDENTICAL // !LANGUAGE: +SuspendConversion // !DIAGNOSTICS: -UNUSED_PARAMETER fun interface SuspendRunnable { - suspend fun invoke() + suspend fun invoke() } fun foo1(s: SuspendRunnable) {} diff --git a/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt index 1bc346b8bd6..4a3c38869ae 100644 --- a/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt +++ b/compiler/testData/diagnostics/tests/unitConversion/chainedFunSuspendUnitConversion.kt @@ -2,7 +2,7 @@ // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION fun interface SuspendRunnable { - suspend fun run() + suspend fun run() } fun foo(r: SuspendRunnable) {}