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 952c403ad97..b4d46683a3b 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 @@ -43,9 +43,7 @@ import org.jetbrains.kotlin.types.checker.NewKotlinTypeChecker import org.jetbrains.kotlin.types.checker.TypeCheckerContext import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo -import org.jetbrains.kotlin.types.typeUtil.asTypeProjection -import org.jetbrains.kotlin.types.typeUtil.builtIns -import org.jetbrains.kotlin.types.typeUtil.contains +import org.jetbrains.kotlin.types.typeUtil.* import javax.inject.Inject class TypeTemplate( @@ -87,8 +85,28 @@ class CoroutineInferenceData { } ?: type } - fun addConstraint(subType: KotlinType, superType: KotlinType) { - csBuilder.addSubtypeConstraint(toNewVariableType(subType), toNewVariableType(superType), ConstraintPositionKind.SPECIAL.position()) + internal fun addConstraint(subType: KotlinType, superType: KotlinType, allowOnlyTrivialConstraints: Boolean) { + val newSubType = toNewVariableType(subType) + val newSuperType = toNewVariableType(superType) + + if (allowOnlyTrivialConstraints) { + if (isTrivialConstraint(subType, superType)) { + // It's important to avoid adding even trivial constraints from extensions, + // because we allow only calls that don't matter at all and here we can get + // into a situation when type is inferred from only trivial constraints to Any?, for example. + + // Actually, this is a more general problem about inferring type without constraints (KT-5464) + return + } else { + badCallHappened() + } + } + + csBuilder.addSubtypeConstraint(newSubType, newSuperType, ConstraintPositionKind.SPECIAL.position()) + } + + private fun isTrivialConstraint(subType: KotlinType, superType: KotlinType): Boolean { + return subType is SimpleType && subType.isNothing() || superType is SimpleType && superType.isNullableAny() } fun reportInferenceResult(externalCSBuilder: ConstraintSystem.Builder) { @@ -201,24 +219,32 @@ class CoroutineInferenceSupport( callCompleter.completeCall(context, overloadResults, tracingStrategy) if (!resultingCall.isReallySuccess()) return - if (!isGoodCall(resultingCall.resultingDescriptor)) { + val resultingDescriptor = resultingCall.resultingDescriptor + if (!isGoodCall(resultingDescriptor)) { inferenceData.badCallHappened() } forceInferenceForArguments(context) { valueArgument: ValueArgument, kotlinType: KotlinType -> - val argumentMatch = resultingCall.getArgumentMapping(valueArgument) as? ArgumentMatch - ?: return@forceInferenceForArguments + val argumentMatch = resultingCall.getArgumentMapping(valueArgument) as? ArgumentMatch ?: return@forceInferenceForArguments with(NewKotlinTypeChecker) { val parameterType = getEffectiveExpectedType(argumentMatch.valueParameter, valueArgument, context) - CoroutineTypeCheckerContext().isSubtypeOf(kotlinType.unwrap(), parameterType.unwrap()) + CoroutineTypeCheckerContext(allowOnlyTrivialConstraints = false).isSubtypeOf(kotlinType.unwrap(), parameterType.unwrap()) } } - val extensionReceiver = resultingCall.resultingDescriptor.extensionReceiverParameter ?: return + val extensionReceiver = resultingDescriptor.extensionReceiverParameter ?: return + val allowOnlyTrivialConstraintsForReceiver = + if (languageVersionSettings.supportsFeature(LanguageFeature.ExperimentalBuilderInference)) + !resultingDescriptor.hasBuilderInferenceAnnotation() + else + false + resultingCall.extensionReceiver?.let { actualReceiver -> with(NewKotlinTypeChecker) { - CoroutineTypeCheckerContext().isSubtypeOf(actualReceiver.type.unwrap(), extensionReceiver.value.type.unwrap()) + CoroutineTypeCheckerContext(allowOnlyTrivialConstraints = allowOnlyTrivialConstraintsForReceiver).isSubtypeOf( + actualReceiver.type.unwrap(), extensionReceiver.value.type.unwrap() + ) } } } @@ -231,7 +257,7 @@ class CoroutineInferenceSupport( } if (resultingDescriptor.isExtension && !resultingDescriptor.hasBuilderInferenceAnnotation()) { - return false + return resultingDescriptor.extensionReceiverParameter?.type?.containsTypeTemplate() == false } val returnType = resultingDescriptor.returnType ?: return false @@ -249,9 +275,13 @@ class CoroutineInferenceSupport( return true } - private class CoroutineTypeCheckerContext : TypeCheckerContext(errorTypeEqualsToAnything = true) { + private class CoroutineTypeCheckerContext( + private val allowOnlyTrivialConstraints: Boolean + ) : TypeCheckerContext(errorTypeEqualsToAnything = true) { + override fun addSubtypeConstraint(subType: UnwrappedType, superType: UnwrappedType): Boolean? { - (subType as? TypeTemplate ?: superType as? TypeTemplate)?.coroutineInferenceData?.addConstraint(subType, superType) + val typeTemplate = subType as? TypeTemplate ?: superType as? TypeTemplate + typeTemplate?.coroutineInferenceData?.addConstraint(subType, superType, allowOnlyTrivialConstraints) return null } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.kt new file mode 100644 index 00000000000..335dd3094da --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.kt @@ -0,0 +1,60 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !USE_EXPERIMENTAL: kotlin.Experimental + +@file:UseExperimental(ExperimentalTypeInference::class) + +import kotlin.experimental.ExperimentalTypeInference + +interface Base + +interface Controller : Base { + suspend fun yield(t: T) {} +} + +fun generate(@BuilderInference g: suspend Controller.() -> Unit): S = TODO() + +fun Base.baseExtension() {} +fun Controller.outNullableAnyExtension() {} +fun Controller.outAnyExtension() {} +fun Controller.invNullableAnyExtension() {} +fun Controller.genericExtension() {} + +@BuilderInference +fun Controller.safeExtension() {} + +val test1 = generate { + yield("foo") + baseExtension() +} + +val test2 = generate { + baseExtension() +} + +val test3 = generate { + yield(42) + outNullableAnyExtension() +} + +val test4 = generate { + outNullableAnyExtension() +} + +val test5 = generate { + yield(42) + outAnyExtension() +} + +val test6 = generate { + yield("bar") + invNullableAnyExtension() +} + +val test7 = generate { + yield("baz") + genericExtension() +} + +val test8 = generate { + safeExtension() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.txt new file mode 100644 index 00000000000..626486cc23a --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.txt @@ -0,0 +1,43 @@ +package + +public val test1: kotlin.String +public val test2: [ERROR : Type for generate { + baseExtension() +}] +public val test3: kotlin.Int +public val test4: [ERROR : Type for generate { + outNullableAnyExtension() +}] +public val test5: [ERROR : Type for generate { + yield(42) + outAnyExtension() +}] +public val test6: [ERROR : Type for generate { + yield("bar") + invNullableAnyExtension() +}] +public val test7: [ERROR : Type for generate { + yield("baz") + genericExtension() +}] +public val test8: kotlin.String +public fun generate(/*0*/ @kotlin.BuilderInference g: suspend Controller.() -> kotlin.Unit): S +public fun Base.baseExtension(): kotlin.Unit +public fun Controller.genericExtension(): kotlin.Unit +public fun Controller.invNullableAnyExtension(): kotlin.Unit +public fun Controller.outAnyExtension(): kotlin.Unit +public fun Controller.outNullableAnyExtension(): kotlin.Unit +@kotlin.BuilderInference public fun Controller.safeExtension(): kotlin.Unit + +public interface Base { + 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 +} + +public interface Controller : Base { + 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 + public open suspend fun yield(/*0*/ t: T): kotlin.Unit +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.kt new file mode 100644 index 00000000000..76e38931f32 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.kt @@ -0,0 +1,50 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER +// !USE_EXPERIMENTAL: kotlin.Experimental + +@file:UseExperimental(ExperimentalTypeInference::class) + +import kotlin.experimental.ExperimentalTypeInference + +interface Base + +interface Controller : Base { + suspend fun yield(t: T) {} +} + +interface SpecificController : Base { + suspend fun yield(t: T) {} +} + +fun generate(@BuilderInference g: suspend Controller.() -> Unit): S = TODO() +fun generateSpecific(@BuilderInference g: suspend SpecificController.() -> Unit): S = TODO() + +fun Base<*>.starBase() {} +fun Base.stringBase() {} + +val test1 = generate { + starBase() + yield("foo") +} + +val test2 = generate { + starBase() +} + +val test3 = generate { + yield("bar") + stringBase() +} + +val test4 = generateSpecific { + yield(42) + starBase() +} + +val test5 = generateSpecific { + yield(42) + stringBase() +} + +val test6 = generateSpecific { + stringBase() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.txt new file mode 100644 index 00000000000..4fb14b3ccda --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.txt @@ -0,0 +1,39 @@ +package + +public val test1: kotlin.String +public val test2: [ERROR : Type for generate { + starBase() +}] +public val test3: [ERROR : Type for generate { + yield("bar") + stringBase() +}] +public val test4: kotlin.Int +public val test5: kotlin.Int +public val test6: [ERROR : Type for generateSpecific { + stringBase() +}] +public fun generate(/*0*/ @kotlin.BuilderInference g: suspend Controller.() -> kotlin.Unit): S +public fun generateSpecific(/*0*/ @kotlin.BuilderInference g: suspend SpecificController.() -> kotlin.Unit): S +public fun Base<*>.starBase(): kotlin.Unit +public fun Base.stringBase(): kotlin.Unit + +public interface Base { + 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 +} + +public interface Controller : Base { + 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 + public open suspend fun yield(/*0*/ t: T): kotlin.Unit +} + +public interface SpecificController : Base { + 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 + public open suspend fun yield(/*0*/ t: T): kotlin.Unit +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt new file mode 100644 index 00000000000..460262f44a4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt @@ -0,0 +1,17 @@ +// !LANGUAGE: -ExperimentalBuilderInference +// !DIAGNOSTICS: -UNUSED_PARAMETER + +interface Base + +interface Controller : Base { + suspend fun yield(t: T) {} +} + +fun generate(g: suspend Controller.() -> Unit): S = TODO() + +suspend fun Base.baseExtension() {} + +val test1 = generate { + yield("foo") + baseExtension() +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.txt new file mode 100644 index 00000000000..0bfcc77262e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.txt @@ -0,0 +1,18 @@ +package + +public val test1: kotlin.String +public fun generate(/*0*/ g: suspend Controller.() -> kotlin.Unit): S +public suspend fun Base.baseExtension(): kotlin.Unit + +public interface Base { + 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 +} + +public interface Controller : Base { + 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 + public open suspend fun yield(/*0*/ t: T): kotlin.Unit +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index fe94c34e164..4bdb7d16cb0 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1747,6 +1747,21 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionSuspend.kt"); } + @TestMetadata("extensionWithNonValuableConstraints.kt") + public void testExtensionWithNonValuableConstraints() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.kt"); + } + + @TestMetadata("extensionsWithNonValuableConstraintsGenericBase.kt") + public void testExtensionsWithNonValuableConstraintsGenericBase() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.kt"); + } + + @TestMetadata("extensionsWithNonValuableConstraints_1_2.kt") + public void testExtensionsWithNonValuableConstraints_1_2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt"); + } + @TestMetadata("incorrectCalls.kt") public void testIncorrectCalls() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/incorrectCalls.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index d2a121566f0..2844d0f53fb 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -1747,6 +1747,21 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionSuspend.kt"); } + @TestMetadata("extensionWithNonValuableConstraints.kt") + public void testExtensionWithNonValuableConstraints() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionWithNonValuableConstraints.kt"); + } + + @TestMetadata("extensionsWithNonValuableConstraintsGenericBase.kt") + public void testExtensionsWithNonValuableConstraintsGenericBase() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraintsGenericBase.kt"); + } + + @TestMetadata("extensionsWithNonValuableConstraints_1_2.kt") + public void testExtensionsWithNonValuableConstraints_1_2() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/extensionsWithNonValuableConstraints_1_2.kt"); + } + @TestMetadata("incorrectCalls.kt") public void testIncorrectCalls() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/incorrectCalls.kt");