Introduce builder-like inference with an explicit opt-in for it

- Add marker for the experimental type inference features
 - Add annotation that will control builder-like inference
 - Require that annotation on corresponding parameters and extensions
 - Allow to use builder inference without suspendability

 Changes in tests and refactorings (rename mainly) are going to be
 introduced in further commits
This commit is contained in:
Mikhail Zarechenskiy
2018-09-18 10:33:14 +03:00
parent 5e9b31ca2c
commit a293aded5d
12 changed files with 205 additions and 12 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.coroutines
import org.jetbrains.kotlin.builtins.isFunctionOrSuspendFunctionType
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -24,3 +25,5 @@ import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
val CallableDescriptor.isSuspendLambda get() = this is AnonymousFunctionDescriptor && this.isSuspend
val ValueParameterDescriptor.hasSuspendFunctionType get() = returnType?.isSuspendFunctionType == true
val ValueParameterDescriptor.hasFunctionOrSuspendFunctionType get() = returnType?.isFunctionOrSuspendFunctionType == true
@@ -6,9 +6,8 @@
package org.jetbrains.kotlin.resolve.calls.inference
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.coroutines.hasSuspendFunctionType
import org.jetbrains.kotlin.coroutines.hasFunctionOrSuspendFunctionType
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
@@ -31,6 +30,8 @@ import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.descriptorUtil.hasBuilderInferenceAnnotation
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
@@ -118,7 +119,7 @@ class CoroutineInferenceSupport(
lambdaExpectedType: KotlinType
) {
val argumentExpression = valueArgument.getArgumentExpression() ?: return
if (!lambdaExpectedType.isSuspendFunctionType) return
if (!lambdaExpectedType.isFunctionOrSuspendFunctionType) return
val lambdaReceiverType = lambdaExpectedType.getReceiverTypeFromFunctionType() ?: return
val inferenceData = CoroutineInferenceData()
@@ -209,16 +210,14 @@ class CoroutineInferenceSupport(
}
private fun isGoodCall(resultingDescriptor: CallableDescriptor): Boolean {
if (resultingDescriptor.isExtension && !resultingDescriptor.hasBuilderInferenceAnnotation()) {
return false
}
fun KotlinType.containsTypeTemplate() = contains { it is TypeTemplate }
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
return !returnType.containsTypeTemplate()
}
private class CoroutineTypeCheckerContext : TypeCheckerContext(errorTypeEqualsToAnything = true) {
@@ -260,11 +259,11 @@ class CoroutineInferenceSupport(
}
fun isCoroutineCallWithAdditionalInference(parameterDescriptor: ValueParameterDescriptor, argument: ValueArgument) =
parameterDescriptor.hasSuspendFunctionType &&
parameterDescriptor.hasBuilderInferenceAnnotation() &&
parameterDescriptor.hasFunctionOrSuspendFunctionType &&
argument.getArgumentExpression() is KtLambdaExpression &&
parameterDescriptor.type.let { it.isBuiltinFunctionalType && it.getReceiverTypeFromFunctionType() != null }
fun OverloadResolutionResultsImpl<*>.isResultWithCoroutineInference() = getCoroutineInferenceData() != null
private fun OverloadResolutionResultsImpl<*>.getCoroutineInferenceData(): CoroutineInferenceData? {
@@ -0,0 +1,37 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: annotation.kt
package kotlin
annotation class BuilderInference
// FILE: test.kt
class Builder<T> {
fun add(t: T) {}
}
fun <S> build(@BuilderInference g: Builder<S>.() -> Unit): List<S> = TODO()
fun <S> wrongBuild(g: Builder<S>.() -> Unit): List<S> = TODO()
fun <S> Builder<S>.extensionAdd(s: S) {}
@BuilderInference
fun <S> Builder<S>.safeExtensionAdd(s: S) {}
val member = build {
add(42)
}
val memberWithoutAnn = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>wrongBuild<!> {
add(42)
}
val extension = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>build<!> {
extensionAdd("foo")
}
val safeExtension = build {
safeExtensionAdd("foo")
}
@@ -0,0 +1,32 @@
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*/ @kotlin.BuilderInference g: 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
@kotlin.BuilderInference public fun </*0*/ S> Builder<S>.safeExtensionAdd(/*0*/ s: S): kotlin.Unit
public final class Builder</*0*/ T> {
public constructor Builder</*0*/ T>()
public final 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
}
package kotlin {
public final annotation class BuilderInference : kotlin.Annotation {
public constructor BuilderInference()
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
}
}
@@ -0,0 +1,32 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// FILE: annotation.kt
package kotlin
annotation class BuilderInference
// FILE: test.kt
class GenericController<T> {
suspend fun yield(t: T) {}
}
suspend fun <S> GenericController<S>.extensionYield(s: S) {}
@BuilderInference
suspend fun <S> GenericController<S>.safeExtensionYield(s: S) {}
fun <S> generate(@BuilderInference g: suspend GenericController<S>.() -> Unit): List<S> = TODO()
val normal = generate {
yield(42)
}
val extension = <!TYPE_INFERENCE_NO_INFORMATION_FOR_PARAMETER!>generate<!> {
extensionYield("foo")
}
val safeExtension = generate {
safeExtensionYield("foo")
}
@@ -0,0 +1,28 @@
package
public val extension: [ERROR : Type for generate {
extensionYield("foo")
}]
public val normal: kotlin.collections.List<kotlin.Int>
public val safeExtension: kotlin.collections.List<kotlin.String>
public fun </*0*/ S> generate(/*0*/ @kotlin.BuilderInference g: suspend GenericController<S>.() -> kotlin.Unit): kotlin.collections.List<S>
public suspend fun </*0*/ S> GenericController<S>.extensionYield(/*0*/ s: S): kotlin.Unit
@kotlin.BuilderInference public suspend fun </*0*/ S> GenericController<S>.safeExtensionYield(/*0*/ s: S): kotlin.Unit
public final class GenericController</*0*/ T> {
public constructor GenericController</*0*/ 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 final suspend fun yield(/*0*/ t: T): kotlin.Unit
}
package kotlin {
public final annotation class BuilderInference : kotlin.Annotation {
public constructor BuilderInference()
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
}
}
@@ -1772,6 +1772,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.kt");
}
@TestMetadata("resolveUsualCallWithBuilderInference.kt")
public void testResolveUsualCallWithBuilderInference() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.kt");
}
@TestMetadata("returnTypeInference.kt")
public void testReturnTypeInference() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/returnTypeInference.kt");
@@ -1802,6 +1807,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/typeFromReceiver.kt");
}
@TestMetadata("useInferenceInformationFromExtension.kt")
public void testUseInferenceInformationFromExtension() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.kt");
}
@TestMetadata("withParameter.kt")
public void testWithParameter() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withParameter.kt");
@@ -1772,6 +1772,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/recursiveGenerators2.kt");
}
@TestMetadata("resolveUsualCallWithBuilderInference.kt")
public void testResolveUsualCallWithBuilderInference() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.kt");
}
@TestMetadata("returnTypeInference.kt")
public void testReturnTypeInference() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/returnTypeInference.kt");
@@ -1802,6 +1807,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/typeFromReceiver.kt");
}
@TestMetadata("useInferenceInformationFromExtension.kt")
public void testUseInferenceInformationFromExtension() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.kt");
}
@TestMetadata("withParameter.kt")
public void testWithParameter() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/withParameter.kt");