Support another one coroutine convention in front-end

- There will be no `coroutine` keyword for builders
- They accept a special suspend function type instead
  (it's return type is straightforward, not Continuation<Unit>)
- Instances of these types may be run with special built-in functions
- These built-ins functions are parametrized
  with handleResult/handleException/interceptResume, so these operators
  become unnecessary (and controllers too)

NB: `@Suspend` annotation is subject to replace with the `suspend` modifier
on types
This commit is contained in:
Denis Zharkov
2016-12-14 20:15:55 +03:00
committed by Stanislav Erokhin
parent 66c2333eb5
commit 1ab003c029
41 changed files with 97 additions and 809 deletions
@@ -16,90 +16,11 @@
package org.jetbrains.kotlin.coroutines
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.expressions.FakeCallKind
import org.jetbrains.kotlin.types.expressions.FakeCallResolver
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.descriptors.isSuspendFunctionType
/**
* @returns type of first value parameter if function is 'operator handleResult' in coroutines controller
*/
fun SimpleFunctionDescriptor.getExpectedTypeForCoroutineControllerHandleResult(): KotlinType? {
if (!isOperator || name != OperatorNameConventions.COROUTINE_HANDLE_RESULT) return null
val CallableDescriptor.isSuspendLambda get() = this is AnonymousFunctionDescriptor && this.isCoroutine
return valueParameters.getOrNull(0)?.type
}
val CallableDescriptor.controllerTypeIfCoroutine: KotlinType?
get() {
if (this !is AnonymousFunctionDescriptor || !this.isCoroutine) return null
return this.extensionReceiverParameter?.returnType
}
fun FakeCallResolver.resolveCoroutineHandleResultCallIfNeeded(
callElement: KtExpression,
expressionToReturn: KtExpression?,
functionDescriptor: FunctionDescriptor,
context: ResolutionContext<*>
) {
functionDescriptor.controllerTypeIfCoroutine ?: return
val info = if (expressionToReturn != null)
context.trace.bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, expressionToReturn)
else
null
val temporaryBindingTrace = TemporaryBindingTrace.create(context.trace, "trace to store fake argument for", "continuation")
val continuation =
ExpressionTypingUtils.createFakeExpressionOfType(
callElement.project, temporaryBindingTrace, "continuation",
// should be Continuation<Nothing>
functionDescriptor.builtIns.nothingType)
fun tryToResolveCall(firstArgument: KtExpression): Boolean {
val resolutionResults = resolveFakeCall(
context.replaceBindingTrace(temporaryBindingTrace), functionDescriptor.extensionReceiverParameter!!.value,
OperatorNameConventions.COROUTINE_HANDLE_RESULT, callElement, callElement, FakeCallKind.OTHER,
listOf(firstArgument, continuation))
if (resolutionResults.isSuccess && resolutionResults.resultingDescriptor.isOperator) {
context.trace.record(BindingContext.RETURN_HANDLE_RESULT_RESOLVED_CALL, callElement, resolutionResults.resultingCall)
return true
}
return false
}
val unitExpression = ExpressionTypingUtils.createFakeExpressionOfType(
callElement.project, temporaryBindingTrace, "unit", functionDescriptor.builtIns.unitType)
val firstArgument =
if (expressionToReturn == null || info != null && info.type != null && KotlinBuiltIns.isUnit(info.type))
unitExpression
else
expressionToReturn
if (!tryToResolveCall(firstArgument) && firstArgument === expressionToReturn) {
tryToResolveCall(unitExpression)
}
}
fun KotlinType.isValidContinuation() =
(constructor.declarationDescriptor as? ClassDescriptor)?.fqNameUnsafe == DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.toUnsafe()
val ValueParameterDescriptor.hasSuspendFunctionType get() = returnType?.isSuspendFunctionType == true
@@ -36,7 +36,6 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.smartcasts.ExplicitSmartCasts;
import org.jetbrains.kotlin.resolve.calls.smartcasts.ImplicitSmartCasts;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.coroutine.CoroutineReceiverValue;
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
import org.jetbrains.kotlin.resolve.scopes.receivers.Qualifier;
@@ -136,7 +135,7 @@ public interface BindingContext {
WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> LOOP_RANGE_NEXT_RESOLVED_CALL = Slices.createSimpleSlice();
WritableSlice<KtExpression, ResolvedCall<FunctionDescriptor>> RETURN_HANDLE_RESULT_RESOLVED_CALL = Slices.createSimpleSlice();
WritableSlice<Call, CoroutineReceiverValue> COROUTINE_RECEIVER_FOR_SUSPENSION_POINT = Slices.createSimpleSlice();
WritableSlice<Call, CallableDescriptor> ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT = Slices.createSimpleSlice();
WritableSlice<Call, SimpleFunctionDescriptor> ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL = Slices.createSimpleSlice();
WritableSlice<VariableAccessorDescriptor, ResolvedCall<FunctionDescriptor>> DELEGATED_PROPERTY_RESOLVED_CALL = Slices.createSimpleSlice();
@@ -20,12 +20,8 @@ import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl;
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.kotlin.resolve.coroutine.CoroutineReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.*;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
import org.jetbrains.kotlin.types.*;
import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
@@ -72,16 +68,6 @@ public class FunctionDescriptorUtil {
) {
ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
if (descriptor instanceof AnonymousFunctionDescriptor
&& (((AnonymousFunctionDescriptor) descriptor).isCoroutine())
&& receiver != null && receiver.getValue() instanceof ExtensionReceiver) {
receiver =
new ReceiverParameterDescriptorImpl(
descriptor,
new CoroutineReceiverValue(
((ExtensionReceiver) receiver.getValue()).getDeclarationDescriptor(), receiver.getValue().getType()));
}
return new LexicalScopeImpl(outerScope, descriptor, true, receiver, LexicalScopeKind.FUNCTION_INNER_SCOPE, redeclarationChecker,
new Function1<LexicalScopeImpl.InitializeHandler, Unit>() {
@Override
@@ -44,8 +44,6 @@ object OperatorModifierChecker {
val checkResult = OperatorChecks.check(functionDescriptor)
if (checkResult.isSuccess) {
when (functionDescriptor.name) {
in COROUTINE_OPERATOR_NAMES ->
checkSupportsFeature(LanguageFeature.Coroutines, languageVersionSettings, diagnosticHolder, modifier)
in REM_TO_MOD_OPERATION_NAMES.keys ->
checkSupportsFeature(LanguageFeature.OperatorRem, languageVersionSettings, diagnosticHolder, modifier)
OperatorNameConventions.PROVIDE_DELEGATE ->
@@ -72,9 +70,3 @@ object OperatorModifierChecker {
}
}
}
private val COROUTINE_OPERATOR_NAMES =
setOf(OperatorNameConventions.COROUTINE_HANDLE_RESULT,
OperatorNameConventions.COROUTINE_HANDLE_EXCEPTION,
OperatorNameConventions.COROUTINE_INTERCEPT_RESUME
)
@@ -20,8 +20,6 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.coroutines.controllerTypeIfCoroutine
import org.jetbrains.kotlin.coroutines.resolveCoroutineHandleResultCallIfNeeded
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.*
@@ -100,8 +98,6 @@ class CallCompleter(
callChecker.check(resolvedCall, reportOn, callCheckerContext)
}
}
resolveHandleResultCallForCoroutineLambdaExpressions(context, resolvedCall)
}
if (results.isSingleResult && results.resultingCall.status.isSuccess) {
@@ -110,27 +106,6 @@ class CallCompleter(
return results
}
private fun <D : CallableDescriptor> resolveHandleResultCallForCoroutineLambdaExpressions(
context: BasicCallResolutionContext,
resolvedCall: ResolvedCall<D>
) {
resolvedCall.valueArguments.values
.flatMap { it.arguments.map { it.getArgumentExpression() } }
.filterIsInstance<KtLambdaExpression>()
.forEach {
val function = context.trace.bindingContext[BindingContext.FUNCTION, it.functionLiteral] ?: return@forEach
function.controllerTypeIfCoroutine ?: return@forEach
val lastBlockStatement = it.functionLiteral.bodyExpression?.statements?.lastOrNull()
// Already resolved
if (lastBlockStatement is KtReturnExpression) return@forEach
fakeCallResolver.resolveCoroutineHandleResultCallIfNeeded(it.functionLiteral, lastBlockStatement, function, context)
}
}
private fun <D : CallableDescriptor> completeAllCandidates(
context: BasicCallResolutionContext,
results: OverloadResolutionResultsImpl<D>
@@ -19,12 +19,8 @@ package org.jetbrains.kotlin.resolve.calls.callResolverUtil
import com.google.common.collect.Lists
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.coroutines.getExpectedTypeForCoroutineControllerHandleResult
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptorImpl
import org.jetbrains.kotlin.incremental.KotlinLookupLocation
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
@@ -37,7 +33,6 @@ import org.jetbrains.kotlin.resolve.calls.inference.getNestedTypeVariables
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
@@ -46,9 +41,7 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.DONT_CARE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.contains
import org.jetbrains.kotlin.util.OperatorNameConventions
enum class ResolveArgumentsMode {
RESOLVE_FUNCTION_ARGUMENTS,
@@ -181,30 +174,6 @@ fun getEffectiveExpectedType(parameterDescriptor: ValueParameterDescriptor, argu
return varargElementType
}
if (parameterDescriptor.isCoroutine &&
argument.getArgumentExpression() is KtLambdaExpression &&
parameterDescriptor.type.isExtensionFunctionType
) {
val receiverType = parameterDescriptor.type.getReceiverTypeFromFunctionType()!!
val newExpectedLambdaReturnType =
receiverType.memberScope
.getContributedFunctions(
OperatorNameConventions.COROUTINE_HANDLE_RESULT, KotlinLookupLocation(argument.asElement())
).mapNotNull {
it.getExpectedTypeForCoroutineControllerHandleResult()
}.singleOrNull()
// If no handleResult function found, then expected return type for lambda is Unit
?: parameterDescriptor.module.builtIns.unitType
// replace return type for lambda with the one we got from single 'handleResult'
val newFunctionTypeArguments = parameterDescriptor.type.arguments.toMutableList()
newFunctionTypeArguments[newFunctionTypeArguments.lastIndex] = newExpectedLambdaReturnType.asTypeProjection()
return parameterDescriptor.type.replace(
newArguments = newFunctionTypeArguments)
}
return parameterDescriptor.type
}
@@ -18,32 +18,40 @@ package org.jetbrains.kotlin.resolve.calls.checkers
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
import org.jetbrains.kotlin.coroutines.isSuspendLambda
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.coroutine.CoroutineReceiverValue
import org.jetbrains.kotlin.resolve.inline.InlineUtil
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.resolve.scopes.LexicalScopeKind
import org.jetbrains.kotlin.resolve.scopes.utils.parentsWithSelf
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object CoroutineSuspendCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.candidateDescriptor as? SimpleFunctionDescriptor ?: return
if (!descriptor.isSuspend) return
val closestCoroutineReceiver =
context.scope.getImplicitReceiversHierarchy().firstOrNull { it.value is CoroutineReceiverValue }?.value as CoroutineReceiverValue?
val (closestSuspendLambdaScope, closestSuspensionLambdaDescriptor) =
context.scope
.parentsWithSelf.firstOrNull {
it is LexicalScope && it.kind == LexicalScopeKind.FUNCTION_INNER_SCOPE &&
it.ownerDescriptor.safeAs<CallableDescriptor>()?.isSuspendLambda == true
}?.let { it to it.cast<LexicalScope>().ownerDescriptor.cast<CallableDescriptor>() }
?: null to null
val enclosingSuspendFunction =
context.scope.parentsWithSelf.filterIsInstance<LexicalScope>().takeWhile {
closestCoroutineReceiver == null || it.implicitReceiver?.value != closestCoroutineReceiver
}.firstOrNull {
(it.ownerDescriptor as? FunctionDescriptor)?.isSuspend == true
}?.ownerDescriptor as? SimpleFunctionDescriptor
context.scope.parentsWithSelf.filterIsInstance<LexicalScope>().takeWhile { it != closestSuspendLambdaScope }
.firstOrNull {
(it.ownerDescriptor as? FunctionDescriptor)?.isSuspend == true
}?.ownerDescriptor as? SimpleFunctionDescriptor
when {
enclosingSuspendFunction != null -> {
@@ -51,14 +59,16 @@ 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)
}
closestCoroutineReceiver != null -> {
closestSuspensionLambdaDescriptor != null -> {
val callElement = resolvedCall.call.callElement as KtExpression
if (!InlineUtil.checkNonLocalReturnUsage(closestCoroutineReceiver.declarationDescriptor, callElement, context.resolutionContext)) {
if (!InlineUtil.checkNonLocalReturnUsage(closestSuspensionLambdaDescriptor, callElement, context.resolutionContext)) {
context.trace.report(Errors.NON_LOCAL_SUSPENSION_POINT.on(reportOn))
}
context.trace.record(BindingContext.COROUTINE_RECEIVER_FOR_SUSPENSION_POINT, resolvedCall.call, closestCoroutineReceiver)
context.trace.record(
BindingContext.ENCLOSING_SUSPEND_LAMBDA_FOR_SUSPENSION_POINT, resolvedCall.call, closestSuspensionLambdaDescriptor
)
}
else -> {
context.trace.report(Errors.ILLEGAL_SUSPEND_FUNCTION_CALL.on(reportOn))
@@ -70,7 +80,7 @@ object CoroutineSuspendCallChecker : CallChecker {
object BuilderFunctionsCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val descriptor = resolvedCall.candidateDescriptor as? FunctionDescriptor ?: return
if (descriptor.valueParameters.any { it.isCoroutine } &&
if (descriptor.valueParameters.any { it.hasSuspendFunctionType } &&
!context.languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) {
context.trace.report(Errors.UNSUPPORTED_FEATURE.on(reportOn, LanguageFeature.Coroutines))
}
@@ -22,7 +22,6 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.coroutines.CoroutineUtilKt;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
@@ -594,9 +593,6 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE);
}
CoroutineUtilKt.resolveCoroutineHandleResultCallIfNeeded(
components.fakeCallResolver, expression, expression.getReturnedExpression(), functionDescriptor, context);
}
else {
context.trace.report(NOT_A_RETURN_LABEL.on(expression, expression.getLabelName()));
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
import org.jetbrains.kotlin.descriptors.isSuspendFunctionType
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Errors.*
@@ -33,7 +34,6 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.BindingContext.EXPECTED_RETURN_TYPE
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getCorrespondingParameterForFunctionArgument
import org.jetbrains.kotlin.resolve.checkers.UnderscoreChecker
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope
@@ -169,7 +169,7 @@ internal class FunctionsTypingVisitor(facade: ExpressionTypingInternals) : Expre
context.scope.ownerDescriptor,
components.annotationResolver.resolveAnnotationsWithArguments(context.scope, expression.getAnnotationEntries(), context.trace),
CallableMemberDescriptor.Kind.DECLARATION, functionLiteral.toSourceElement(),
expression.getCorrespondingParameterForFunctionArgument(context.trace.bindingContext)?.isCoroutine ?: false
!noExpectedType(context.expectedType) && context.expectedType.isSuspendFunctionType
)
components.functionDescriptorResolver.
initializeFunctionDescriptorAndExplicitReturnType(context.scope.ownerDescriptor, context.scope, functionLiteral,