Allow destructuring in suspend lambda with suspend componentX
#KT-16113 Fixed
This commit is contained in:
@@ -332,15 +332,15 @@ public class DescriptorResolver {
|
|||||||
public List<VariableDescriptor> invoke() {
|
public List<VariableDescriptor> invoke() {
|
||||||
assert owner.getDispatchReceiverParameter() == null
|
assert owner.getDispatchReceiverParameter() == null
|
||||||
: "Destructuring declarations are only be parsed for lambdas, and they must not have a dispatch receiver";
|
: "Destructuring declarations are only be parsed for lambdas, and they must not have a dispatch receiver";
|
||||||
LexicalScope scopeWithReceiver =
|
LexicalScope scopeForDestructuring =
|
||||||
ScopeUtilsKt.addImplicitReceiver(scope, owner.getExtensionReceiverParameter());
|
ScopeUtilsKt.createScopeForDestructuring(scope, owner.getExtensionReceiverParameter());
|
||||||
|
|
||||||
List<VariableDescriptor> result =
|
List<VariableDescriptor> result =
|
||||||
destructuringDeclarationResolver.resolveLocalVariablesFromDestructuringDeclaration(
|
destructuringDeclarationResolver.resolveLocalVariablesFromDestructuringDeclaration(
|
||||||
scope,
|
scope,
|
||||||
destructuringDeclaration, new TransientReceiver(type), /* initializer = */ null,
|
destructuringDeclaration, new TransientReceiver(type), /* initializer = */ null,
|
||||||
ExpressionTypingContext.newContext(
|
ExpressionTypingContext.newContext(
|
||||||
trace, scopeWithReceiver, DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE
|
trace, scopeForDestructuring, DataFlowInfoFactory.EMPTY, TypeUtils.NO_EXPECTED_TYPE
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.config.LanguageVersionSettings
|
|||||||
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
|
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
@@ -41,6 +40,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.cast
|
|||||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
object CoroutineSuspendCallChecker : CallChecker {
|
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) {
|
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||||
val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return
|
val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return
|
||||||
if (!descriptor.isSuspend) return
|
if (!descriptor.isSuspend) return
|
||||||
@@ -48,7 +49,7 @@ object CoroutineSuspendCallChecker : CallChecker {
|
|||||||
val enclosingSuspendFunction =
|
val enclosingSuspendFunction =
|
||||||
context.scope
|
context.scope
|
||||||
.parentsWithSelf.firstOrNull {
|
.parentsWithSelf.firstOrNull {
|
||||||
it is LexicalScope && it.kind == LexicalScopeKind.FUNCTION_INNER_SCOPE &&
|
it is LexicalScope && it.kind in ALLOWED_SCOPE_KINDS &&
|
||||||
it.ownerDescriptor.safeAs<FunctionDescriptor>()?.isSuspend == true
|
it.ownerDescriptor.safeAs<FunctionDescriptor>()?.isSuspend == true
|
||||||
}?.cast<LexicalScope>()?.ownerDescriptor?.cast<FunctionDescriptor>()
|
}?.cast<LexicalScope>()?.ownerDescriptor?.cast<FunctionDescriptor>()
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ enum class LexicalScopeKind(val withLocalDescriptors: Boolean) {
|
|||||||
PROPERTY_DELEGATE_METHOD(false),
|
PROPERTY_DELEGATE_METHOD(false),
|
||||||
|
|
||||||
FUNCTION_HEADER(false),
|
FUNCTION_HEADER(false),
|
||||||
|
FUNCTION_HEADER_FOR_DESTRUCTURING(false),
|
||||||
FUNCTION_INNER_SCOPE(true),
|
FUNCTION_INNER_SCOPE(true),
|
||||||
|
|
||||||
TYPE_ALIAS_HEADER(false),
|
TYPE_ALIAS_HEADER(false),
|
||||||
|
|||||||
@@ -201,9 +201,12 @@ fun LexicalScope.replaceImportingScopes(importingScopeChain: ImportingScope?): L
|
|||||||
return LexicalScopeWrapper(this, newImportingScopeChain)
|
return LexicalScopeWrapper(this, newImportingScopeChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun LexicalScope.addImplicitReceiver(newReceiver: ReceiverParameterDescriptor?): LexicalScope {
|
fun LexicalScope.createScopeForDestructuring(newReceiver: ReceiverParameterDescriptor?): LexicalScope {
|
||||||
if (newReceiver == null) return this
|
return LexicalScopeImpl(
|
||||||
return LexicalScopeImpl(parent, ownerDescriptor, isOwnerDescriptorAccessibleByLabel, newReceiver, kind)
|
parent, ownerDescriptor, isOwnerDescriptorAccessibleByLabel,
|
||||||
|
newReceiver ?: implicitReceiver,
|
||||||
|
LexicalScopeKind.FUNCTION_HEADER_FOR_DESTRUCTURING
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LexicalScopeWrapper(val delegate: LexicalScope, val newImportingScopeChain: ImportingScope): LexicalScope by delegate {
|
private class LexicalScopeWrapper(val delegate: LexicalScope, val newImportingScopeChain: ImportingScope): LexicalScope by delegate {
|
||||||
|
|||||||
Vendored
+26
@@ -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
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
@kotlin.Metadata
|
||||||
|
public final class A {
|
||||||
|
private final @org.jetbrains.annotations.NotNull field o: java.lang.String
|
||||||
|
public method <init>(@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 <init>(): void
|
||||||
|
public method <init>(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.experimental.CoroutineContext): void
|
||||||
|
public synthetic method <init>(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 <init>(): 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
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// SKIP_TXT
|
||||||
|
class A {
|
||||||
|
suspend operator fun component1(): String = "K"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(<!UNUSED_PARAMETER!>c<!>: suspend (A) -> Unit) {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
foo {
|
||||||
|
(x) ->
|
||||||
|
x.length
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -5056,6 +5056,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("tailrec.kt")
|
||||||
public void testTailrec() throws Exception {
|
public void testTailrec() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
||||||
|
|||||||
@@ -791,6 +791,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("suspendExternalFunctions.kt")
|
||||||
public void testSuspendExternalFunctions() throws Exception {
|
public void testSuspendExternalFunctions() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendExternalFunctions.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendExternalFunctions.kt");
|
||||||
|
|||||||
@@ -5056,6 +5056,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("tailrec.kt")
|
||||||
public void testTailrec() throws Exception {
|
public void testTailrec() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
||||||
|
|||||||
@@ -5056,6 +5056,12 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("tailrec.kt")
|
||||||
public void testTailrec() throws Exception {
|
public void testTailrec() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
||||||
|
|||||||
+12
@@ -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.");
|
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")
|
@TestMetadata("tailrec.kt")
|
||||||
public void testTailrec() throws Exception {
|
public void testTailrec() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/featureIntersection/tailrec.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user