diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt index b05634fd28c..a49e8a7a300 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/GenericCandidateResolver.kt @@ -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) } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt index 264d76200ac..952c403ad97 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/inference/CoroutineInferenceUtil.kt @@ -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 diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionStatelessCallbacksImpl.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionStatelessCallbacksImpl.kt index b1bbb7093d1..22a5fe1aea7 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionStatelessCallbacksImpl.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/calls/tower/KotlinResolutionStatelessCallbacksImpl.kt @@ -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()?.variableCall override fun isCoroutineCall(argument: KotlinCallArgument, parameter: ValueParameterDescriptor): Boolean = - isCoroutineCallWithAdditionalInference(parameter, argument.psiCallArgument.valueArgument) + isCoroutineCallWithAdditionalInference(parameter, argument.psiCallArgument.valueArgument, languageVersionSettings) } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.kt new file mode 100644 index 00000000000..64db29d733e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.kt @@ -0,0 +1,29 @@ +// !LANGUAGE: -ExperimentalBuilderInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +class Builder { + suspend fun add(t: T) {} +} + +fun build(g: suspend Builder.() -> Unit): List = TODO() +fun wrongBuild(g: Builder.() -> Unit): List = TODO() + +fun Builder.extensionAdd(s: S) {} + +suspend fun Builder.safeExtensionAdd(s: S) {} + +val member = build { + add(42) +} + +val memberWithoutAnn = wrongBuild { + add(42) +} + +val extension = build { + extensionAdd("foo") +} + +val safeExtension = build { + safeExtensionAdd("foo") +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.txt new file mode 100644 index 00000000000..d490a4e90bf --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/inferCoroutineTypeInOldVersion.txt @@ -0,0 +1,22 @@ +package + +public val extension: [ERROR : Type for build { + extensionAdd("foo") +}] +public val member: kotlin.collections.List +public val memberWithoutAnn: [ERROR : Type for wrongBuild { + add(42) +}] +public val safeExtension: kotlin.collections.List +public fun build(/*0*/ g: suspend Builder.() -> kotlin.Unit): kotlin.collections.List +public fun wrongBuild(/*0*/ g: Builder.() -> kotlin.Unit): kotlin.collections.List +public fun Builder.extensionAdd(/*0*/ s: S): kotlin.Unit +public suspend fun Builder.safeExtensionAdd(/*0*/ s: S): kotlin.Unit + +public final class Builder { + public constructor Builder() + 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 330e4ca324e..4fd1ac6e5a7 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index d03c5ec3716..e9e9e79aa30 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -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"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index a1c76f8a443..d6ee2022b95 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.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),