Allow destructuring in suspend lambda with suspend componentX

#KT-16113 Fixed
This commit is contained in:
Denis Zharkov
2017-02-02 10:58:29 +03:00
parent 59fe7444d1
commit 60c2579436
12 changed files with 132 additions and 8 deletions
@@ -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
)
);
@@ -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 {
@@ -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
}
@@ -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
}
@@ -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
}
}
@@ -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");
@@ -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");