From a293aded5da16757f04f3a177415dc35b7b121e2 Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Tue, 18 Sep 2018 10:33:14 +0300 Subject: [PATCH] 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 --- .../kotlin/coroutines/coroutineUtil.kt | 3 ++ .../calls/inference/CoroutineInferenceUtil.kt | 23 ++++++------ .../resolveUsualCallWithBuilderInference.kt | 37 +++++++++++++++++++ .../resolveUsualCallWithBuilderInference.txt | 32 ++++++++++++++++ .../useInferenceInformationFromExtension.kt | 32 ++++++++++++++++ .../useInferenceInformationFromExtension.txt | 28 ++++++++++++++ .../DiagnosticsTestWithStdLibGenerated.java | 10 +++++ ...ticsTestWithStdLibUsingJavacGenerated.java | 10 +++++ ...mRuntimeDescriptorLoaderTestGenerated.java | 5 +++ .../kotlin/resolve/annotationsForResolve.kt | 4 ++ .../src/kotlin/annotations/Inference.kt | 18 +++++++++ .../kotlin/experimental/inferenceMarker.kt | 15 ++++++++ 12 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.txt create mode 100644 libraries/stdlib/src/kotlin/annotations/Inference.kt create mode 100644 libraries/stdlib/src/kotlin/experimental/inferenceMarker.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/coroutines/coroutineUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/coroutines/coroutineUtil.kt index 143c2b0b0fe..0a1d3e433ab 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/coroutines/coroutineUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/coroutines/coroutineUtil.kt @@ -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 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 2e865a3ee78..264d76200ac 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,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? { diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.kt new file mode 100644 index 00000000000..ad8321fa4d6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.kt @@ -0,0 +1,37 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +// FILE: annotation.kt + +package kotlin + +annotation class BuilderInference + +// FILE: test.kt + +class Builder { + fun add(t: T) {} +} + +fun build(@BuilderInference g: Builder.() -> Unit): List = TODO() +fun wrongBuild(g: Builder.() -> Unit): List = TODO() + +fun Builder.extensionAdd(s: S) {} + +@BuilderInference +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/resolveUsualCallWithBuilderInference.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.txt new file mode 100644 index 00000000000..09f79370be6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/resolveUsualCallWithBuilderInference.txt @@ -0,0 +1,32 @@ +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*/ @kotlin.BuilderInference g: 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 +@kotlin.BuilderInference public fun Builder.safeExtensionAdd(/*0*/ s: S): kotlin.Unit + +public final class Builder { + public constructor Builder() + 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 + } +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.kt new file mode 100644 index 00000000000..9d93e272c11 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.kt @@ -0,0 +1,32 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +// FILE: annotation.kt + +package kotlin + +annotation class BuilderInference + +// FILE: test.kt + +class GenericController { + suspend fun yield(t: T) {} +} + +suspend fun GenericController.extensionYield(s: S) {} + +@BuilderInference +suspend fun GenericController.safeExtensionYield(s: S) {} + +fun generate(@BuilderInference g: suspend GenericController.() -> Unit): List = TODO() + +val normal = generate { + yield(42) +} + +val extension = generate { + extensionYield("foo") +} + +val safeExtension = generate { + safeExtensionYield("foo") +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.txt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.txt new file mode 100644 index 00000000000..bdb5071930e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/useInferenceInformationFromExtension.txt @@ -0,0 +1,28 @@ +package + +public val extension: [ERROR : Type for generate { + extensionYield("foo") +}] +public val normal: kotlin.collections.List +public val safeExtension: kotlin.collections.List +public fun generate(/*0*/ @kotlin.BuilderInference g: suspend GenericController.() -> kotlin.Unit): kotlin.collections.List +public suspend fun GenericController.extensionYield(/*0*/ s: S): kotlin.Unit +@kotlin.BuilderInference public suspend fun GenericController.safeExtensionYield(/*0*/ s: S): kotlin.Unit + +public final class GenericController { + public constructor GenericController() + 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 + } +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 591ec4b31a0..330e4ca324e 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -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"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 6e21688ca13..d03c5ec3716 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -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"); diff --git a/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java b/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java index ddd6ce3ae27..1ce394f1058 100644 --- a/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java +++ b/core/descriptors.runtime/tests/org/jetbrains/kotlin/jvm/runtime/JvmRuntimeDescriptorLoaderTestGenerated.java @@ -145,6 +145,11 @@ public class JvmRuntimeDescriptorLoaderTestGenerated extends AbstractJvmRuntimeD runTest("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/PropertyField.kt"); } + @TestMetadata("PublishedApiAnnotationOnInlineClassCosntructor.kt") + public void testPublishedApiAnnotationOnInlineClassCosntructor() throws Exception { + runTest("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/PublishedApiAnnotationOnInlineClassCosntructor.kt"); + } + @TestMetadata("Setter.kt") public void testSetter() throws Exception { runTest("compiler/testData/loadJava/compiledKotlin/annotations/classMembers/Setter.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt index 80059a63a45..dd4480c070c 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/annotationsForResolve.kt @@ -19,6 +19,7 @@ private val LOW_PRIORITY_IN_OVERLOAD_RESOLUTION_FQ_NAME = FqName("kotlin.interna private val HIDES_MEMBERS_ANNOTATION_FQ_NAME = FqName("kotlin.internal.HidesMembers") private val ONLY_INPUT_TYPES_FQ_NAME = FqName("kotlin.internal.OnlyInputTypes") private val DYNAMIC_EXTENSION_FQ_NAME = FqName("kotlin.internal.DynamicExtension") +private val BUILDER_INFERENCE_ANNOTATION_FQ_NAME = FqName("kotlin.BuilderInference") // @HidesMembers annotation only has effect for members with these names val HIDES_MEMBERS_NAME_LIST = setOf(Name.identifier("forEach")) @@ -39,6 +40,9 @@ fun CallableDescriptor.hasDynamicExtensionAnnotation(): Boolean = annotations.ha fun TypeParameterDescriptor.hasOnlyInputTypesAnnotation(): Boolean = annotations.hasAnnotation(ONLY_INPUT_TYPES_FQ_NAME) +fun CallableDescriptor.hasBuilderInferenceAnnotation(): Boolean = + annotations.hasAnnotation(BUILDER_INFERENCE_ANNOTATION_FQ_NAME) + fun getExactInAnnotations(): Annotations = AnnotationsWithOnly(EXACT_ANNOTATION_FQ_NAME) private class AnnotationsWithOnly(val presentAnnotation: FqName): Annotations { diff --git a/libraries/stdlib/src/kotlin/annotations/Inference.kt b/libraries/stdlib/src/kotlin/annotations/Inference.kt new file mode 100644 index 00000000000..6fc4db97325 --- /dev/null +++ b/libraries/stdlib/src/kotlin/annotations/Inference.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin + +import kotlin.annotation.AnnotationTarget.* +import kotlin.experimental.ExperimentalTypeInference + +/** + * Marks the API which usages is dependent on the experimental builder inference. + */ +@Target(VALUE_PARAMETER, FUNCTION, PROPERTY) +@Retention(AnnotationRetention.BINARY) +@SinceKotlin("1.3") +@ExperimentalTypeInference +public annotation class BuilderInference diff --git a/libraries/stdlib/src/kotlin/experimental/inferenceMarker.kt b/libraries/stdlib/src/kotlin/experimental/inferenceMarker.kt new file mode 100644 index 00000000000..6c8dc9db493 --- /dev/null +++ b/libraries/stdlib/src/kotlin/experimental/inferenceMarker.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package kotlin.experimental + +/** + * Marker of the use experimental type inference features + */ +@Experimental(level = Experimental.Level.ERROR) +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.ANNOTATION_CLASS) +@SinceKotlin("1.3") +public annotation class ExperimentalTypeInference