From c5aeaae8e63ef53ba5d5601ec60239e49c0f84b6 Mon Sep 17 00:00:00 2001 From: Stanislav Erokhin Date: Thu, 15 Dec 2016 16:17:32 +0300 Subject: [PATCH] Support explicit `this` receiver (`this.foo()`) for RestrictSuspension function call. --- .../calls/checkers/coroutineCallChecker.kt | 24 +++++++++---- .../restrictSuspension/sameInstance.kt | 36 ++++++++++++++----- .../restrictSuspension/sameInstance.txt | 1 + 3 files changed, 45 insertions(+), 16 deletions(-) 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 e1d0fdb4610..fac0d1e1579 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 @@ -28,12 +28,14 @@ import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.diagnostics.DiagnosticSink import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtThisExpression import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.hasRestrictSuspensionAnnotation import org.jetbrains.kotlin.resolve.inline.InlineUtil import org.jetbrains.kotlin.resolve.scopes.LexicalScope import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf import org.jetbrains.kotlin.types.typeUtil.supertypes @@ -65,7 +67,7 @@ object CoroutineSuspendCallChecker : CallChecker { // Here we only record enclosing function mapping (for backends purposes) context.trace.record(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.call, enclosingSuspendFunction) - checkRestrictSuspension(enclosingSuspendFunction.extensionReceiverParameter, resolvedCall, reportOn, context) + checkRestrictSuspension(enclosingSuspendFunction, resolvedCall, reportOn, context) } closestSuspensionLambdaDescriptor != null -> { val callElement = resolvedCall.call.callElement as KtExpression @@ -78,7 +80,7 @@ object CoroutineSuspendCallChecker : CallChecker { BindingContext.ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT, resolvedCall.call, closestSuspensionLambdaDescriptor ) - checkRestrictSuspension(closestSuspensionLambdaDescriptor.extensionReceiverParameter, resolvedCall, reportOn, context) + checkRestrictSuspension(closestSuspensionLambdaDescriptor, resolvedCall, reportOn, context) } else -> { context.trace.report(Errors.ILLEGAL_SUSPEND_FUNCTION_CALL.on(reportOn)) @@ -109,20 +111,28 @@ fun checkCoroutinesFeature(languageVersionSettings: LanguageVersionSettings, dia } private fun checkRestrictSuspension( - enclosingSuspendReceiver: ReceiverParameterDescriptor?, + enclosingCallableDescriptor: CallableDescriptor, resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext ) { - if (enclosingSuspendReceiver == null) return - val enclosingSuspendReceiverValue = enclosingSuspendReceiver.value + val enclosingSuspendReceiverValue = enclosingCallableDescriptor.extensionReceiverParameter?.value ?: return fun ReceiverValue.isRestrictSuspensionReceiver() = (type.supertypes() + type).any { it.constructor.declarationDescriptor?.hasRestrictSuspensionAnnotation() == true } - // todo explicit this and implicit this is not equals now - infix fun ReceiverValue.sameInstance(other: ReceiverValue?) = this === other + infix fun ReceiverValue.sameInstance(other: ReceiverValue?): Boolean { + if (other == null) return false + if (this === other) return true + + val referenceExpression = ((other as? ExpressionReceiver)?.expression as? KtThisExpression)?.instanceReference + val referenceTarget = referenceExpression?.let { + context.trace.get(BindingContext.REFERENCE_TARGET, referenceExpression) + } + + return this === (referenceTarget as? CallableDescriptor)?.extensionReceiverParameter?.value + } if (!enclosingSuspendReceiverValue.isRestrictSuspensionReceiver()) return diff --git a/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.kt b/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.kt index d5d4703e51a..75aa522f692 100644 --- a/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.kt +++ b/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.kt @@ -14,22 +14,40 @@ fun test() { member() extension() - // todo - this.member() - this.extension() + this.member() + this.extension() val foo = this foo.member() foo.extension() - // todo - this@l.member() - this@l.extension() + this@l.member() + this@l.extension() with(1) { - // todo - this@l.member() - this@l.extension() + this@l.member() + this@l.extension() } } +} + +suspend fun RestrictedController.l() { + member() + extension() + + this.member() + this.extension() + + val foo = this + foo.member() + foo.extension() + + this@l.member() + this@l.extension() + + with(1) { + this@l.member() + this@l.extension() + } + } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.txt b/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.txt index d9a1ef457a9..c08866af291 100644 --- a/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.txt +++ b/compiler/testData/diagnostics/tests/coroutines/restrictSuspension/sameInstance.txt @@ -3,6 +3,7 @@ package public fun generate(/*0*/ f: suspend RestrictedController.() -> kotlin.Unit): kotlin.Unit public fun test(): kotlin.Unit public suspend fun RestrictedController.extension(): kotlin.Unit +public suspend fun RestrictedController.l(): kotlin.Unit @kotlin.coroutines.RestrictSuspension public final class RestrictedController { public constructor RestrictedController()