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)
}
@@ -0,0 +1,29 @@
// !LANGUAGE: -ExperimentalBuilderInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
class Builder<T> {
suspend fun add(t: T) {}
}
fun <S> build(g: suspend Builder<S>.() -> Unit): List<S> = TODO()
fun <S> wrongBuild(g: Builder<S>.() -> Unit): List<S> = TODO()
fun <S> Builder<S>.extensionAdd(s: S) {}
suspend fun <S> Builder<S>.safeExtensionAdd(s: S) {}
val member = build {
add(42)
}
val memberWithoutAnn = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>wrongBuild<!> {
<!ILLEGAL_SUSPEND_FUNCTION_CALL!>add<!>(42)
}
val extension = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>build<!> {
extensionAdd("foo")
}
val safeExtension = build {
safeExtensionAdd("foo")
}
@@ -0,0 +1,22 @@
package
public val extension: [ERROR : Type for build {
extensionAdd("foo")
}]
public val member: kotlin.collections.List<kotlin.Int>
public val memberWithoutAnn: [ERROR : Type for wrongBuild {
add(42)
}]
public val safeExtension: kotlin.collections.List<kotlin.String>
public fun </*0*/ S> build(/*0*/ g: suspend Builder<S>.() -> kotlin.Unit): kotlin.collections.List<S>
public fun </*0*/ S> wrongBuild(/*0*/ g: Builder<S>.() -> kotlin.Unit): kotlin.collections.List<S>
public fun </*0*/ S> Builder<S>.extensionAdd(/*0*/ s: S): kotlin.Unit
public suspend fun </*0*/ S> Builder<S>.safeExtensionAdd(/*0*/ s: S): kotlin.Unit
public final class Builder</*0*/ T> {
public constructor Builder</*0*/ T>()
public final suspend fun add(/*0*/ t: T): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -1752,6 +1752,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/incorrectCalls.kt");
}
@TestMetadata("inferCoroutineTypeInOldVersion.kt")
public void testInferCoroutineTypeInOldVersion() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.kt");
}
@TestMetadata("kt15516.kt")
public void testKt15516() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt15516.kt");
@@ -1752,6 +1752,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/incorrectCalls.kt");
}
@TestMetadata("inferCoroutineTypeInOldVersion.kt")
public void testInferCoroutineTypeInOldVersion() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.kt");
}
@TestMetadata("kt15516.kt")
public void testKt15516() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt15516.kt");
@@ -89,6 +89,7 @@ enum class LanguageFeature(
ProhibitErroneousExpressionsInAnnotationsWithUseSiteTargets(KOTLIN_1_3, kind = BUG_FIX),
NewCapturedReceiverFieldNamingConvention(KOTLIN_1_3, kind = BUG_FIX),
ExtendedMainConvention(KOTLIN_1_3),
ExperimentalBuilderInference(KOTLIN_1_3),
RestrictReturnStatementTarget(KOTLIN_1_4, kind = BUG_FIX),
NoConstantValueAttributeForNonConstVals(KOTLIN_1_4, kind = BUG_FIX),