Allow using extensions with trivial-constraints in builder-inference
#KT-27079 Fixed
This commit is contained in:
+44
-14
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// !USE_EXPERIMENTAL: kotlin.Experimental
|
||||
|
||||
@file:UseExperimental(ExperimentalTypeInference::class)
|
||||
|
||||
import kotlin.experimental.ExperimentalTypeInference
|
||||
|
||||
interface Base
|
||||
|
||||
interface Controller<T> : Base {
|
||||
suspend fun yield(t: T) {}
|
||||
}
|
||||
|
||||
fun <S> generate(@BuilderInference g: suspend Controller<S>.() -> Unit): S = TODO()
|
||||
|
||||
fun Base.baseExtension() {}
|
||||
fun Controller<out Any?>.outNullableAnyExtension() {}
|
||||
fun Controller<out Any>.outAnyExtension() {}
|
||||
fun Controller<Any?>.invNullableAnyExtension() {}
|
||||
fun <S> Controller<S>.genericExtension() {}
|
||||
|
||||
@BuilderInference
|
||||
fun Controller<String>.safeExtension() {}
|
||||
|
||||
val test1 = generate {
|
||||
yield("foo")
|
||||
baseExtension()
|
||||
}
|
||||
|
||||
val test2 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
|
||||
baseExtension()
|
||||
}
|
||||
|
||||
val test3 = generate {
|
||||
yield(42)
|
||||
outNullableAnyExtension()
|
||||
}
|
||||
|
||||
val test4 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
|
||||
outNullableAnyExtension()
|
||||
}
|
||||
|
||||
val test5 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
|
||||
yield(42)
|
||||
outAnyExtension()
|
||||
}
|
||||
|
||||
val test6 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
|
||||
yield("bar")
|
||||
invNullableAnyExtension()
|
||||
}
|
||||
|
||||
val test7 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
|
||||
yield("baz")
|
||||
genericExtension<Int>()
|
||||
}
|
||||
|
||||
val test8 = generate {
|
||||
safeExtension()
|
||||
}
|
||||
+43
@@ -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<Int>()
|
||||
}]
|
||||
public val test8: kotlin.String
|
||||
public fun </*0*/ S> generate(/*0*/ @kotlin.BuilderInference g: suspend Controller<S>.() -> kotlin.Unit): S
|
||||
public fun Base.baseExtension(): kotlin.Unit
|
||||
public fun </*0*/ S> Controller<S>.genericExtension(): kotlin.Unit
|
||||
public fun Controller<kotlin.Any?>.invNullableAnyExtension(): kotlin.Unit
|
||||
public fun Controller<out kotlin.Any>.outAnyExtension(): kotlin.Unit
|
||||
public fun Controller<out kotlin.Any?>.outNullableAnyExtension(): kotlin.Unit
|
||||
@kotlin.BuilderInference public fun Controller<kotlin.String>.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</*0*/ T> : 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
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// !USE_EXPERIMENTAL: kotlin.Experimental
|
||||
|
||||
@file:UseExperimental(ExperimentalTypeInference::class)
|
||||
|
||||
import kotlin.experimental.ExperimentalTypeInference
|
||||
|
||||
interface Base<K>
|
||||
|
||||
interface Controller<T> : Base<T> {
|
||||
suspend fun yield(t: T) {}
|
||||
}
|
||||
|
||||
interface SpecificController<T> : Base<String> {
|
||||
suspend fun yield(t: T) {}
|
||||
}
|
||||
|
||||
fun <S> generate(@BuilderInference g: suspend Controller<S>.() -> Unit): S = TODO()
|
||||
fun <S> generateSpecific(@BuilderInference g: suspend SpecificController<S>.() -> Unit): S = TODO()
|
||||
|
||||
fun Base<*>.starBase() {}
|
||||
fun Base<String>.stringBase() {}
|
||||
|
||||
val test1 = generate {
|
||||
starBase()
|
||||
yield("foo")
|
||||
}
|
||||
|
||||
val test2 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
|
||||
starBase()
|
||||
}
|
||||
|
||||
val test3 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
|
||||
yield("bar")
|
||||
stringBase()
|
||||
}
|
||||
|
||||
val test4 = generateSpecific {
|
||||
yield(42)
|
||||
starBase()
|
||||
}
|
||||
|
||||
val test5 = generateSpecific {
|
||||
yield(42)
|
||||
stringBase()
|
||||
}
|
||||
|
||||
val test6 = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generateSpecific<!> {
|
||||
stringBase()
|
||||
}
|
||||
+39
@@ -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 </*0*/ S> generate(/*0*/ @kotlin.BuilderInference g: suspend Controller<S>.() -> kotlin.Unit): S
|
||||
public fun </*0*/ S> generateSpecific(/*0*/ @kotlin.BuilderInference g: suspend SpecificController<S>.() -> kotlin.Unit): S
|
||||
public fun Base<*>.starBase(): kotlin.Unit
|
||||
public fun Base<kotlin.String>.stringBase(): kotlin.Unit
|
||||
|
||||
public interface Base</*0*/ K> {
|
||||
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</*0*/ T> : Base<T> {
|
||||
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</*0*/ T> : Base<kotlin.String> {
|
||||
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
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// !LANGUAGE: -ExperimentalBuilderInference
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
|
||||
interface Base
|
||||
|
||||
interface Controller<T> : Base {
|
||||
suspend fun yield(t: T) {}
|
||||
}
|
||||
|
||||
fun <S> generate(g: suspend Controller<S>.() -> Unit): S = TODO()
|
||||
|
||||
suspend fun Base.baseExtension() {}
|
||||
|
||||
val test1 = generate {
|
||||
yield("foo")
|
||||
baseExtension()
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package
|
||||
|
||||
public val test1: kotlin.String
|
||||
public fun </*0*/ S> generate(/*0*/ g: suspend Controller<S>.() -> 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</*0*/ T> : 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
|
||||
}
|
||||
+15
@@ -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");
|
||||
|
||||
compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java
Generated
+15
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user