Support coroutine inference under version <= 1.2
Follow-up #KT-26958
This commit is contained in:
+1
-1
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+44
-6
@@ -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
|
||||
|
||||
|
||||
+4
-2
@@ -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)
|
||||
}
|
||||
|
||||
compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.kt
Vendored
+29
@@ -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")
|
||||
}
|
||||
+22
@@ -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
|
||||
}
|
||||
+5
@@ -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");
|
||||
|
||||
compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java
Generated
+5
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user