Support coroutine inference under version <= 1.2

Follow-up #KT-26958
This commit is contained in:
Mikhail Zarechenskiy
2018-09-20 13:07:38 +03:00
parent de36d919aa
commit a4f5cced47
8 changed files with 111 additions and 9 deletions
@@ -414,7 +414,7 @@ class GenericCandidateResolver(
val effectiveExpectedType = getEffectiveExpectedType(valueParameterDescriptor, valueArgument, context)
if (isCoroutineCallWithAdditionalInference(valueParameterDescriptor, valueArgument)) {
if (isCoroutineCallWithAdditionalInference(valueParameterDescriptor, valueArgument, languageVersionSettings)) {
coroutineInferenceSupport.analyzeCoroutine(functionLiteral, valueArgument, constraintSystem, context, effectiveExpectedType)
}
@@ -6,8 +6,12 @@
package org.jetbrains.kotlin.resolve.calls.inference
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.coroutines.hasFunctionOrSuspendFunctionType
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.psi.KtExpression
@@ -111,6 +115,8 @@ class CoroutineInferenceSupport(
@set:Inject
lateinit var callCompleter: CallCompleter
private val languageVersionSettings get() = expressionTypingServices.languageVersionSettings
fun analyzeCoroutine(
functionLiteral: KtFunction,
valueArgument: ValueArgument,
@@ -119,7 +125,8 @@ class CoroutineInferenceSupport(
lambdaExpectedType: KotlinType
) {
val argumentExpression = valueArgument.getArgumentExpression() ?: return
if (!lambdaExpectedType.isFunctionOrSuspendFunctionType) return
if (!checkExpectedTypeForArgument(lambdaExpectedType)) return
val lambdaReceiverType = lambdaExpectedType.getReceiverTypeFromFunctionType() ?: return
val inferenceData = CoroutineInferenceData()
@@ -173,6 +180,13 @@ class CoroutineInferenceSupport(
inferenceData.reportInferenceResult(csBuilder)
}
private fun checkExpectedTypeForArgument(expectedType: KotlinType): Boolean {
return if (languageVersionSettings.supportsFeature(LanguageFeature.ExperimentalBuilderInference))
expectedType.isFunctionOrSuspendFunctionType
else
expectedType.isSuspendFunctionType
}
fun checkCoroutineCalls(
context: BasicCallResolutionContext,
tracingStrategy: TracingStrategy,
@@ -209,17 +223,32 @@ class CoroutineInferenceSupport(
}
}
private fun KotlinType.containsTypeTemplate() = contains { it is TypeTemplate }
private fun isGoodCall(resultingDescriptor: CallableDescriptor): Boolean {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ExperimentalBuilderInference)) {
return isGoodCallForOldCoroutines(resultingDescriptor)
}
if (resultingDescriptor.isExtension && !resultingDescriptor.hasBuilderInferenceAnnotation()) {
return false
}
fun KotlinType.containsTypeTemplate() = contains { it is TypeTemplate }
val returnType = resultingDescriptor.returnType ?: return false
return !returnType.containsTypeTemplate()
}
private fun isGoodCallForOldCoroutines(resultingDescriptor: CallableDescriptor): Boolean {
val returnType = resultingDescriptor.returnType ?: return false
if (returnType.containsTypeTemplate()) return false
if (resultingDescriptor !is FunctionDescriptor || resultingDescriptor.isSuspend) return true
if (resultingDescriptor.valueParameters.any { it.type.containsTypeTemplate() }) return false
return true
}
private class CoroutineTypeCheckerContext : TypeCheckerContext(errorTypeEqualsToAnything = true) {
override fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? {
(subType as? TypeTemplate ?: superType as? TypeTemplate)?.coroutineInferenceData?.addConstraint(subType, superType)
@@ -258,11 +287,20 @@ class CoroutineInferenceSupport(
}
}
fun isCoroutineCallWithAdditionalInference(parameterDescriptor: ValueParameterDescriptor, argument: ValueArgument) =
parameterDescriptor.hasBuilderInferenceAnnotation() &&
parameterDescriptor.hasFunctionOrSuspendFunctionType &&
fun isCoroutineCallWithAdditionalInference(
parameterDescriptor: ValueParameterDescriptor,
argument: ValueArgument,
languageVersionSettings: LanguageVersionSettings
): Boolean {
val parameterHasOptIn = if (languageVersionSettings.supportsFeature(LanguageFeature.ExperimentalBuilderInference))
parameterDescriptor.hasBuilderInferenceAnnotation() && parameterDescriptor.hasFunctionOrSuspendFunctionType
else
parameterDescriptor.hasSuspendFunctionType
return parameterHasOptIn &&
argument.getArgumentExpression() is KtLambdaExpression &&
parameterDescriptor.type.let { it.isBuiltinFunctionalType && it.getReceiverTypeFromFunctionType() != null }
}
fun OverloadResolutionResultsImpl<*>.isResultWithCoroutineInference() = getCoroutineInferenceData() != null
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.resolve.calls.tower
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -35,7 +36,8 @@ import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class KotlinResolutionStatelessCallbacksImpl(
private val deprecationResolver: DeprecationResolver
private val deprecationResolver: DeprecationResolver,
private val languageVersionSettings: LanguageVersionSettings
) : KotlinResolutionStatelessCallbacks {
override fun isDescriptorFromSource(descriptor: CallableDescriptor) =
DescriptorToSourceUtils.descriptorToDeclaration(descriptor) != null
@@ -70,5 +72,5 @@ class KotlinResolutionStatelessCallbacksImpl(
functionCall.safeAs<PSIKotlinCallForInvoke>()?.variableCall
override fun isCoroutineCall(argument: KotlinCallArgument, parameter: ValueParameterDescriptor): Boolean =
isCoroutineCallWithAdditionalInference(parameter, argument.psiCallArgument.valueArgument)
isCoroutineCallWithAdditionalInference(parameter, argument.psiCallArgument.valueArgument, languageVersionSettings)
}