Allow using extensions with trivial-constraints in builder-inference

#KT-27079 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-09-24 11:35:15 +03:00
parent 0da1b9b80f
commit 0d103e7f0c
9 changed files with 301 additions and 14 deletions
@@ -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
}
}
@@ -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()
}
@@ -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
}
@@ -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()
}
@@ -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
}
@@ -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()
}
@@ -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
}
@@ -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");
@@ -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");