diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java index 8ee4eaa165d..5304354182f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/DescriptorResolver.java @@ -332,15 +332,15 @@ public class DescriptorResolver { public List invoke() { assert owner.getDispatchReceiverParameter() == null : "Destructuring declarations are only be parsed for lambdas, and they must not have a dispatch receiver"; - LexicalScope scopeWithReceiver = - ScopeUtilsKt.addImplicitReceiver(scope, owner.getExtensionReceiverParameter()); + LexicalScope scopeForDestructuring = + ScopeUtilsKt.createScopeForDestructuring(scope, owner.getExtensionReceiverParameter()); List result = destructuringDeclarationResolver.resolveLocalVariablesFromDestructuringDeclaration( scope, destructuringDeclaration, new TransientReceiver(type), /* initializer = */ null, ExpressionTypingContext.newContext( - trace, scopeWithReceiver, DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE + trace, scopeForDestructuring, DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE ) ); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt index 26355a92573..14e522fc572 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/coroutineCallChecker.kt @@ -22,7 +22,6 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.psi.KtExpression @@ -41,6 +40,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.kotlin.utils.addToStdlib.safeAs object CoroutineSuspendCallChecker : CallChecker { + private val ALLOWED_SCOPE_KINDS = setOf(LexicalScopeKind.FUNCTION_INNER_SCOPE, LexicalScopeKind.FUNCTION_HEADER_FOR_DESTRUCTURING) + override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) { val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return if (!descriptor.isSuspend) return @@ -48,7 +49,7 @@ object CoroutineSuspendCallChecker : CallChecker { val enclosingSuspendFunction = context.scope .parentsWithSelf.firstOrNull { - it is LexicalScope && it.kind == LexicalScopeKind.FUNCTION_INNER_SCOPE && + it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS && it.ownerDescriptor.safeAs()?.isSuspend == true }?.cast()?.ownerDescriptor?.cast() diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt index 7f969e119f9..2727ce6e883 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/Scopes.kt @@ -80,6 +80,7 @@ enum class LexicalScopeKind(val withLocalDescriptors: Boolean) { PROPERTY_DELEGATE_METHOD(false), FUNCTION_HEADER(false), + FUNCTION_HEADER_FOR_DESTRUCTURING(false), FUNCTION_INNER_SCOPE(true), TYPE_ALIAS_HEADER(false), diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt index 82cf01b8417..256c46e6148 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/scopes/utils/ScopeUtils.kt @@ -201,9 +201,12 @@ fun LexicalScope.replaceImportingScopes(importingScopeChain: ImportingScope?): L return LexicalScopeWrapper(this, newImportingScopeChain) } -fun LexicalScope.addImplicitReceiver(newReceiver: ReceiverParameterDescriptor?): LexicalScope { - if (newReceiver == null) return this - return LexicalScopeImpl(parent, ownerDescriptor, isOwnerDescriptorAccessibleByLabel, newReceiver, kind) +fun LexicalScope.createScopeForDestructuring(newReceiver: ReceiverParameterDescriptor?): LexicalScope { + return LexicalScopeImpl( + parent, ownerDescriptor, isOwnerDescriptorAccessibleByLabel, + newReceiver ?: implicitReceiver, + LexicalScopeKind.FUNCTION_HEADER_FOR_DESTRUCTURING + ) } private class LexicalScopeWrapper(val delegate: LexicalScope, val newImportingScopeChain: ImportingScope): LexicalScope by delegate { diff --git a/compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt b/compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt new file mode 100644 index 00000000000..fec3447128a --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt @@ -0,0 +1,26 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND: JS +import kotlin.coroutines.experimental.* +import kotlin.coroutines.experimental.intrinsics.* + +data class A(val o: String) { + operator suspend fun component2(): String = suspendCoroutineOrReturn { x -> + x.resume("K") + COROUTINE_SUSPENDED + } +} +fun builder(c: suspend (A) -> Unit) { + (c as (suspend A.() -> Unit)).startCoroutine(A("O"), EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + (x, y) -> + result = x + y + } + + return result +} diff --git a/compiler/testData/codegen/light-analysis/coroutines/featureIntersection/suspendDestructuringInLambdas.txt b/compiler/testData/codegen/light-analysis/coroutines/featureIntersection/suspendDestructuringInLambdas.txt new file mode 100644 index 00000000000..7451ba03806 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/coroutines/featureIntersection/suspendDestructuringInLambdas.txt @@ -0,0 +1,44 @@ +@kotlin.Metadata +public final class A { + private final @org.jetbrains.annotations.NotNull field o: java.lang.String + public method (@org.jetbrains.annotations.NotNull p0: java.lang.String): void + public final @org.jetbrains.annotations.NotNull method component1(): java.lang.String + public final @org.jetbrains.annotations.Nullable method component2(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.Continuation): java.lang.Object + public synthetic static method copy$default(p0: A, p1: java.lang.String, p2: int, p3: java.lang.Object): A + public final @org.jetbrains.annotations.NotNull method copy(@org.jetbrains.annotations.NotNull p0: java.lang.String): A + public method equals(p0: java.lang.Object): boolean + public final @org.jetbrains.annotations.NotNull method getO(): java.lang.String + public method hashCode(): int + public method toString(): java.lang.String +} + +@kotlin.Metadata +public final class CoroutineUtilKt { + public final static @org.jetbrains.annotations.NotNull method handleExceptionContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation + public final static @org.jetbrains.annotations.NotNull method handleResultContinuation(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): kotlin.coroutines.experimental.Continuation +} + +@kotlin.Metadata +public class EmptyContinuation { + public final static field Companion: EmptyContinuation.Companion + private final @org.jetbrains.annotations.NotNull field context: kotlin.coroutines.experimental.CoroutineContext + inner class EmptyContinuation/Companion + public @synthetic.kotlin.jvm.GeneratedByJvmOverloads method (): void + public method (@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.CoroutineContext): void + public synthetic method (p0: kotlin.coroutines.experimental.CoroutineContext, p1: int, p2: kotlin.jvm.internal.DefaultConstructorMarker): void + public @org.jetbrains.annotations.NotNull method getContext(): kotlin.coroutines.experimental.CoroutineContext + public method resume(@org.jetbrains.annotations.Nullable p0: java.lang.Object): void + public method resumeWithException(@org.jetbrains.annotations.NotNull p0: java.lang.Throwable): void +} + +@kotlin.Metadata +public final static class EmptyContinuation/Companion { + inner class EmptyContinuation/Companion + private method (): void +} + +@kotlin.Metadata +public final class SuspendDestructuringInLambdasKt { + 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.Function2): void +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendDestructuring.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendDestructuring.kt new file mode 100644 index 00000000000..533b3f276bc --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendDestructuring.kt @@ -0,0 +1,13 @@ +// SKIP_TXT +class A { + suspend operator fun component1(): String = "K" +} + +fun foo(c: suspend (A) -> Unit) {} + +fun bar() { + foo { + (x) -> + x.length + } +} 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 78b3c3b0c4b..1d87044f2ad 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 @@ -5056,6 +5056,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("suspendDestructuringInLambdas.kt") + public void testSuspendDestructuringInLambdas() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt"); + doTest(fileName); + } + @TestMetadata("tailrec.kt") public void testTailrec() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index dfc9ceeb785..bbdbc4bd03c 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -791,6 +791,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW doTest(fileName); } + @TestMetadata("suspendDestructuring.kt") + public void testSuspendDestructuring() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendDestructuring.kt"); + doTest(fileName); + } + @TestMetadata("suspendExternalFunctions.kt") public void testSuspendExternalFunctions() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendExternalFunctions.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 0483e5bf4c7..a937719911d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5056,6 +5056,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("suspendDestructuringInLambdas.kt") + public void testSuspendDestructuringInLambdas() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt"); + doTest(fileName); + } + @TestMetadata("tailrec.kt") public void testTailrec() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index f72b1a66f0d..029e884dabd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -5056,6 +5056,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("suspendDestructuringInLambdas.kt") + public void testSuspendDestructuringInLambdas() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.kt"); + doTest(fileName); + } + @TestMetadata("tailrec.kt") public void testTailrec() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt"); 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 b122ac4402b..3b140c4b0ca 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 @@ -5747,6 +5747,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("suspendDestructuringInLambdas.kt") + public void testSuspendDestructuringInLambdas() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/suspendDestructuringInLambdas.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("tailrec.kt") public void testTailrec() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");