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() {
|
||||
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<VariableDescriptor> 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
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
+3
-2
@@ -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<FunctionDescriptor>()?.isSuspend == true
|
||||
}?.cast<LexicalScope>()?.ownerDescriptor?.cast<FunctionDescriptor>()
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
+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.");
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
Reference in New Issue
Block a user