From 39fc1789e058e00b2b6bd1113edfb2fd6e02ff36 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 31 Jan 2017 11:11:14 +0300 Subject: [PATCH] Prohibit inline lambda parameters of suspend function type #KT-16068 Fixed --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../checkers/InlineParameterChecker.kt | 9 +++- .../inlineTwoReceivers.kt | 2 +- .../coroutines/suspendFunctionType/inline.kt | 45 +++++++++++++++++++ .../DiagnosticsTestWithStdLibGenerated.java | 6 +++ 6 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 6dabbda745c..551974b08ce 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -193,6 +193,7 @@ public interface Errors { DiagnosticFactory2 WRONG_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(ERROR); DiagnosticFactory2 DEPRECATED_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(WARNING); DiagnosticFactory1 ILLEGAL_INLINE_PARAMETER_MODIFIER = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 WRONG_ANNOTATION_TARGET = DiagnosticFactory1.create(ERROR); DiagnosticFactory2 WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET = DiagnosticFactory2.create(ERROR); DiagnosticFactory0 REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR); 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 5fd2b9d3637..76dc680d056 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -159,6 +159,7 @@ public class DefaultErrorMessages { MAP.put(WRONG_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is not applicable inside ''{1}''", TO_STRING, TO_STRING); MAP.put(DEPRECATED_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is deprecated inside ''{1}''", TO_STRING, TO_STRING); MAP.put(ILLEGAL_INLINE_PARAMETER_MODIFIER, "Modifier ''{0}'' is allowed only for function parameters of an inline function", TO_STRING); + MAP.put(INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED, "Inline lambda parameters of suspend function type are not fully supported. Add 'noinline' modifier."); MAP.put(WRONG_ANNOTATION_TARGET, "This annotation is not applicable to target ''{0}''", TO_STRING); MAP.put(WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET, "This annotation is not applicable to target ''{0}'' and use site target ''@{1}''", TO_STRING, TO_STRING); MAP.put(REPEATED_ANNOTATION, "This annotation is not repeatable"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt index a21f603b4c6..fee026a0808 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/InlineParameterChecker.kt @@ -16,7 +16,9 @@ package org.jetbrains.kotlin.resolve.checkers +import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.builtins.isSuspendFunctionType import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors @@ -35,10 +37,15 @@ object InlineParameterChecker : SimpleDeclarationChecker { val inline = declaration.hasModifier(KtTokens.INLINE_KEYWORD) for (parameter in declaration.valueParameters) { val parameterDescriptor = bindingContext.get(BindingContext.VALUE_PARAMETER, parameter) - if (!inline || (parameterDescriptor != null && !parameterDescriptor.type.isFunctionType)) { + if (!inline || (parameterDescriptor != null && !parameterDescriptor.type.isBuiltinFunctionalType)) { parameter.reportIncorrectInline(KtTokens.NOINLINE_KEYWORD, diagnosticHolder) parameter.reportIncorrectInline(KtTokens.CROSSINLINE_KEYWORD, diagnosticHolder) } + + if (inline && !parameter.hasModifier(KtTokens.NOINLINE_KEYWORD) && + parameterDescriptor?.type?.isSuspendFunctionType == true) { + diagnosticHolder.report(Errors.INLINE_SUSPEND_FUNCTION_TYPE_UNSUPPORTED.on(parameter)) + } } } } diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inlineTwoReceivers.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inlineTwoReceivers.kt index a321b8a798c..08e71a5cbba 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inlineTwoReceivers.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsCoroutine/inlineTwoReceivers.kt @@ -12,7 +12,7 @@ class MyTest { } } -inline suspend fun testAsync(routine: suspend MyTest.() -> T): T = routine(MyTest()) +inline suspend fun testAsync(noinline routine: suspend MyTest.() -> T): T = routine(MyTest()) inline suspend fun Iterable.test() = testAsync { var sum = "" diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt new file mode 100644 index 00000000000..4962f727b97 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt @@ -0,0 +1,45 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// SKIP_TXT +inline fun foo1(x: suspend () -> Unit) {} +inline fun foo2(crossinline x: suspend () -> Unit) {} +inline fun foo3(noinline x: suspend () -> Unit) {} +inline fun foo4(noinline crossinline x: suspend () -> Unit) {} + +suspend inline fun bar1(x: suspend () -> Unit) {} +suspend inline fun bar2(crossinline x: suspend () -> Unit) {} +suspend inline fun bar3(noinline x: suspend () -> Unit) {} +suspend inline fun bar4(noinline crossinline x: suspend () -> Unit) {} + +suspend fun baz() { + foo1 { + return@baz + } + + foo2 { + return@baz + } + + foo3 { + return@baz + } + + foo4 { + return@baz + } + + bar1 { + return@baz + } + + bar2 { + return@baz + } + + bar3 { + return@baz + } + + bar4 { + return@baz + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 31d4234a037..8acc0238ee4 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -979,6 +979,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW doTest(fileName); } + @TestMetadata("inline.kt") + public void testInline() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/inline.kt"); + doTest(fileName); + } + @TestMetadata("invoke.kt") public void testInvoke() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.kt");