From fa9653c3d29c7bae185a57bbb2fb57c5ecc0c14f Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Thu, 19 Jul 2018 21:49:28 +0300 Subject: [PATCH] Deserialize experimental coroutines as suspend functions/types but deprecate them for now. Promote stub version. #KT-25623: Fixed --- .../kotlin/resolve/deprecationUtil.kt | 13 +++ .../kotlin/psi/stubs/KotlinStubVersions.kt | 2 +- .../serialization/DescriptorSerializer.kt | 4 +- .../library/experimental.kt | 27 +++++++ .../output.txt | 29 +++++++ .../release.kt | 12 +++ .../coroutines/builder.kt | 14 ++-- .../coroutines/lambda.kt | 26 +++--- .../coroutines/receiver.kt | 8 +- .../coroutines/simple.kt | 30 +++---- .../languageVersionIsNotEqualToApiVersion.kt | 6 +- .../CompileKotlinAgainstCustomBinariesTest.kt | 14 ++++ .../kotlin/builtins/suspendFunctionTypes.kt | 34 ++++---- .../deserialization/MemberDeserializer.kt | 81 ++++++++----------- .../deserialization/TypeDeserializer.kt | 33 ++++---- .../DeserializedMemberDescriptor.kt | 22 +++-- 16 files changed, 228 insertions(+), 127 deletions(-) create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/library/experimental.kt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/output.txt create mode 100644 compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt index e79af8b1aa0..849d425f4a4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/deprecationUtil.kt @@ -103,6 +103,13 @@ private data class DeprecatedByOverridden(private val deprecations: Collection type.contains(UnwrappedType::isSuspendFunctionType) } + ).any { type -> type.contains(UnwrappedType::isSuspendFunctionTypeOrSubtype) } } fun typeAliasProto(descriptor: TypeAliasDescriptor): ProtoBuf.TypeAlias.Builder { diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/library/experimental.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/library/experimental.kt new file mode 100644 index 00000000000..2010ad4e448 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/library/experimental.kt @@ -0,0 +1,27 @@ +suspend fun dummy() {} + +class C { + suspend fun dummy() = "OK" +} + +class WithNested { + class Nested { + suspend fun dummy() = "OK" + } +} + +class WithInner { + inner class Inner { + suspend fun dummy() = "OK" + } +} + +val c: suspend () -> Unit = {} + +class WithTypeParameter Unit> {} + +fun returnsSuspend() : suspend() -> Unit = {} + +fun builder(c: suspend () -> Unit) {} + +fun Unit> withTypeParameter() = {} \ No newline at end of file diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/output.txt b/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/output.txt new file mode 100644 index 00000000000..3097f812483 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/output.txt @@ -0,0 +1,29 @@ +warning: language version 1.3 is experimental, there are no backwards compatibility guarantees for new language and library features +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:2:5: error: using 'c: suspend () -> Unit' is an error. Experimental coroutine cannot be used with API version 1.3 + c() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:3:5: error: using 'constructor WithTypeParameter Unit>()' is an error. Experimental coroutine cannot be used with API version 1.3 + WithTypeParameter Unit>() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:4:5: error: using 'returnsSuspend(): suspend () -> Unit' is an error. Experimental coroutine cannot be used with API version 1.3 + returnsSuspend() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:5:5: error: using 'builder(suspend () -> Unit): Unit' is an error. Experimental coroutine cannot be used with API version 1.3 + builder {} + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:6:5: error: using 'withTypeParameter(): () -> Unit' is an error. Experimental coroutine cannot be used with API version 1.3 + withTypeParameter Unit>() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:8:5: error: using 'dummy(): Unit' is an error. Experimental coroutine cannot be used with API version 1.3 + dummy() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:9:9: error: using 'dummy(): String' is an error. Experimental coroutine cannot be used with API version 1.3 + C().dummy() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:10:25: error: using 'dummy(): String' is an error. Experimental coroutine cannot be used with API version 1.3 + WithNested.Nested().dummy() + ^ +compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:11:25: error: using 'dummy(): String' is an error. Experimental coroutine cannot be used with API version 1.3 + WithInner().Inner().dummy() + ^ +COMPILATION_ERROR diff --git a/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt b/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt new file mode 100644 index 00000000000..f3f4a20e43c --- /dev/null +++ b/compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt @@ -0,0 +1,12 @@ +suspend fun callRelease() { + c() + WithTypeParameter Unit>() + returnsSuspend() + builder {} + withTypeParameter Unit>() + + dummy() + C().dummy() + WithNested.Nested().dummy() + WithInner().Inner().dummy() +} diff --git a/compiler/testData/compileKotlinAgainstKotlin/coroutines/builder.kt b/compiler/testData/compileKotlinAgainstKotlin/coroutines/builder.kt index 346435428ce..df62ac25af7 100644 --- a/compiler/testData/compileKotlinAgainstKotlin/coroutines/builder.kt +++ b/compiler/testData/compileKotlinAgainstKotlin/coroutines/builder.kt @@ -1,5 +1,7 @@ // FILE: A.kt // LANGUAGE_VERSION: 1.2 +// TODO: Unmute when automatic conversion experimental <-> release will be implemented +// IGNORE_BACKEND: JVM, JS, NATIVE, JVM_IR, JS_IR import kotlin.coroutines.experimental.* fun builder1(c: suspend () -> String): String { @@ -39,11 +41,11 @@ fun ok(continuation: Continuation): Any? { } fun box(): String { - if (builder1(::ok) != "OK") return "FAIL 1" - if (builder1 { cont: Continuation -> "OK" } != "OK") return "FAIL 2" - if (builder1(fun (cont: Continuation): Any? = "OK") != "OK") return "FAIL 3" - - if (builder2 { cont: Continuation -> this + "K" } != "OK") return "FAIL 5" - if (builder2(fun String.(cont: Continuation): Any? = this + "K") != "OK") return "FAIL 6" +// if (builder1(::ok) != "OK") return "FAIL 1" +// if (builder1 { cont: Continuation -> "OK" } != "OK") return "FAIL 2" +// if (builder1(fun (cont: Continuation): Any? = "OK") != "OK") return "FAIL 3" +// +// if (builder2 { cont: Continuation -> this + "K" } != "OK") return "FAIL 5" +// if (builder2(fun String.(cont: Continuation): Any? = this + "K") != "OK") return "FAIL 6" return "OK" } diff --git a/compiler/testData/compileKotlinAgainstKotlin/coroutines/lambda.kt b/compiler/testData/compileKotlinAgainstKotlin/coroutines/lambda.kt index ce058b232fa..8bd6990413f 100644 --- a/compiler/testData/compileKotlinAgainstKotlin/coroutines/lambda.kt +++ b/compiler/testData/compileKotlinAgainstKotlin/coroutines/lambda.kt @@ -1,5 +1,7 @@ // FILE: A.kt // LANGUAGE_VERSION: 1.2 +// TODO: Unmute when automatic conversion experimental <-> release will be implemented +// IGNORE_BACKEND: JVM, JS, NATIVE, JVM_IR, JS_IR val dummy1: suspend () -> String = { "OK" } val dummy2: suspend String.() -> String = { this + "K" } @@ -10,17 +12,17 @@ val dummy3: suspend String.(String) -> String = { s -> this + s } import kotlin.coroutines.experimental.* fun box(): String { - val continuation = object : Continuation { - override val context = EmptyCoroutineContext - override fun resume(value: String) { - } - - override fun resumeWithException(exception: Throwable) { - throw exception - } - } - if (dummy1(continuation) != "OK") return "FAIL 1" - if ("O".dummy2(continuation) != "OK") return "FAIL 2" - if ("O".dummy3("K", continuation) != "OK") return "FAIL 3" +// val continuation = object : Continuation { +// override val context = EmptyCoroutineContext +// override fun resume(value: String) { +// } +// +// override fun resumeWithException(exception: Throwable) { +// throw exception +// } +// } +// if (dummy1(continuation) != "OK") return "FAIL 1" +// if ("O".dummy2(continuation) != "OK") return "FAIL 2" +// if ("O".dummy3("K", continuation) != "OK") return "FAIL 3" return "OK" } diff --git a/compiler/testData/compileKotlinAgainstKotlin/coroutines/receiver.kt b/compiler/testData/compileKotlinAgainstKotlin/coroutines/receiver.kt index 1dc10159345..9d328958de3 100644 --- a/compiler/testData/compileKotlinAgainstKotlin/coroutines/receiver.kt +++ b/compiler/testData/compileKotlinAgainstKotlin/coroutines/receiver.kt @@ -1,5 +1,7 @@ // FILE: A.kt // LANGUAGE_VERSION: 1.2 +// TODO: Unmute when automatic conversion experimental <-> release will be implemented +// IGNORE_BACKEND: JVM, JS, NATIVE, JVM_IR, JS_IR import kotlin.coroutines.experimental.* fun (suspend () -> String).builder(): String { @@ -25,8 +27,8 @@ fun ok(continuation: Continuation): Any? { } fun box(): String { - if ((::ok).builder() != "OK") return "FAIL 1" - if (({ cont: Continuation -> "OK" }).builder() != "OK") return "FAIL 2" - if ((fun (cont: Continuation): Any? = "OK").builder() != "OK") return "FAIL 3" +// if ((::ok).builder() != "OK") return "FAIL 1" +// if (({ cont: Continuation -> "OK" }).builder() != "OK") return "FAIL 2" +// if ((fun (cont: Continuation): Any? = "OK").builder() != "OK") return "FAIL 3" return "OK" } diff --git a/compiler/testData/compileKotlinAgainstKotlin/coroutines/simple.kt b/compiler/testData/compileKotlinAgainstKotlin/coroutines/simple.kt index 31c454888a6..f16643055ac 100644 --- a/compiler/testData/compileKotlinAgainstKotlin/coroutines/simple.kt +++ b/compiler/testData/compileKotlinAgainstKotlin/coroutines/simple.kt @@ -1,5 +1,7 @@ // FILE: A.kt // LANGUAGE_VERSION: 1.2 +// TODO: Unmute when automatic conversion experimental <-> release will be implemented +// IGNORE_BACKEND: JVM, JS, NATIVE, JVM_IR, JS_IR suspend fun dummy() = "OK" @@ -29,19 +31,19 @@ class WithInner { import kotlin.coroutines.experimental.* fun box(): String { - val continuation = object : Continuation { - override val context = EmptyCoroutineContext - override fun resume(value: String) { - } - override fun resumeWithException(exception: Throwable) { - throw exception - } - } - if (dummy(continuation) != "OK") return "FAIL 1" - if ("O".dummy(continuation) != "OK") return "FAIL 2" - if ("O".dummy("K", continuation) != "OK") return "FAIL 3" - if (C().dummy(continuation) != "OK") return "FAIL 4" - if (WithNested.Nested().dummy(continuation) != "OK") return "FAIL 5" - if (WithInner().Inner().dummy(continuation) != "OK") return "FAIL 6" +// val continuation = object : Continuation { +// override val context = EmptyCoroutineContext +// override fun resume(value: String) { +// } +// override fun resumeWithException(exception: Throwable) { +// throw exception +// } +// } +// if (dummy(continuation) != "OK") return "FAIL 1" +// if ("O".dummy(continuation) != "OK") return "FAIL 2" +// if ("O".dummy("K", continuation) != "OK") return "FAIL 3" +// if (C().dummy(continuation) != "OK") return "FAIL 4" +// if (WithNested.Nested().dummy(continuation) != "OK") return "FAIL 5" +// if (WithInner().Inner().dummy(continuation) != "OK") return "FAIL 6" return "OK" } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/release/languageVersionIsNotEqualToApiVersion.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/release/languageVersionIsNotEqualToApiVersion.kt index 9a292e52e59..f7d8dbb9ade 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/release/languageVersionIsNotEqualToApiVersion.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/release/languageVersionIsNotEqualToApiVersion.kt @@ -24,14 +24,14 @@ fun builder(c: suspend () -> Unit) {} } fun test2() { - kotlin.coroutines.experimental.buildSequence { - yield(1) + kotlin.coroutines.experimental.buildSequence { + yield(1) } kotlin.sequences.buildSequence { yield(1) } } -suspend fun test3(): Unit = kotlin.coroutines.experimental.suspendCoroutine { _ -> Unit } +suspend fun test3(): Unit = kotlin.coroutines.experimental.suspendCoroutine { _ -> Unit } suspend fun test4(): Unit = kotlin.coroutines.suspendCoroutine { _ -> Unit } \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt index a6506a950be..d022840a475 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstCustomBinariesTest.kt @@ -457,6 +457,20 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration ) } + fun testExperimentalCoroutineCallFromRelease() { + val library = compileLibrary( + "library", + additionalOptions = listOf("-language-version", "1.2"), + checkKotlinOutput = {} + ) + compileKotlin( + "release.kt", + tmpdir, + listOf(library), + additionalOptions = listOf("-language-version", "1.3", "-api-version", "1.3") + ) + } + companion object { // compiler before 1.1.4 version did not include suspension marks into bytecode. private fun stripSuspensionMarksToImitateLegacyCompiler(bytes: ByteArray): Pair { diff --git a/core/descriptors/src/org/jetbrains/kotlin/builtins/suspendFunctionTypes.kt b/core/descriptors/src/org/jetbrains/kotlin/builtins/suspendFunctionTypes.kt index bba3eb7a625..b0a516307d6 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/builtins/suspendFunctionTypes.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/builtins/suspendFunctionTypes.kt @@ -54,7 +54,6 @@ val FAKE_CONTINUATION_CLASS_DESCRIPTOR_RELEASE = createTypeConstructor() } - fun transformSuspendFunctionToRuntimeFunctionType(suspendFunType: KotlinType, isReleaseCoroutines: Boolean): SimpleType { assert(suspendFunType.isSuspendFunctionType) { "This type should be suspend function type: $suspendFunType" @@ -80,30 +79,35 @@ fun transformSuspendFunctionToRuntimeFunctionType(suspendFunType: KotlinType, is ).makeNullableAsSpecified(suspendFunType.isMarkedNullable) } -fun transformRuntimeFunctionTypeToSuspendFunction(funType: KotlinType, isReleaseCoroutines: Boolean): SimpleType? { +fun transformRuntimeFunctionTypeToSuspendFunction(funType: KotlinType, isReleaseCoroutines: Boolean): Pair { assert(funType.isFunctionType) { "This type should be function type: $funType" } - val continuationArgumentType = funType.getValueParameterTypesFromFunctionType().lastOrNull()?.type ?: return null - if (!isContinuation(continuationArgumentType.constructor.declarationDescriptor?.fqNameSafe, isReleaseCoroutines) || - continuationArgumentType.arguments.size != 1 + val continuationArgumentType = funType.getValueParameterTypesFromFunctionType().lastOrNull()?.type ?: return null to false + val continuationArgumentFqName = continuationArgumentType.constructor.declarationDescriptor?.fqNameSafe + if (continuationArgumentType.arguments.size != 1 || + (!isContinuation(continuationArgumentFqName, isReleaseCoroutines) && + (!isReleaseCoroutines || !isContinuation(continuationArgumentFqName, !isReleaseCoroutines))) ) { - return funType as? SimpleType + return funType as? SimpleType to false } val suspendReturnType = continuationArgumentType.arguments.single().type + // Load experimental suspend function type as suspend function type + return createFunctionType( - funType.builtIns, - funType.annotations, - funType.getReceiverTypeFromFunctionType(), - funType.getValueParameterTypesFromFunctionType().dropLast(1).map(TypeProjection::getType), - // TODO: names - null, - suspendReturnType, - suspendFunction = true - ).makeNullableAsSpecified(funType.isMarkedNullable) + funType.builtIns, + funType.annotations, + funType.getReceiverTypeFromFunctionType(), + funType.getValueParameterTypesFromFunctionType().dropLast(1).map(TypeProjection::getType), + // TODO: names + null, + suspendReturnType, + suspendFunction = true + ).makeNullableAsSpecified(funType.isMarkedNullable) to + (isReleaseCoroutines && isContinuation(continuationArgumentFqName, !isReleaseCoroutines)) } fun isContinuation(name: FqName?, isReleaseCoroutines: Boolean): Boolean { diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt index 3943093da51..344350ead9e 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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 org.jetbrains.kotlin.serialization.deserialization @@ -26,13 +15,9 @@ import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.* -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.protobuf.MessageLite import org.jetbrains.kotlin.resolve.DescriptorFactory -import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.serialization.deserialization.descriptors.* -import org.jetbrains.kotlin.types.KotlinType class MemberDeserializer(private val c: DeserializationContext) { private val annotationDeserializer = AnnotationDeserializer(c.components.moduleDescriptor, c.components.notFoundClasses) @@ -75,6 +60,12 @@ class MemberDeserializer(private val c: DeserializationContext) { proto.receiverType(c.typeTable)?.let { local.typeDeserializer.type(it, receiverAnnotations) } ) + // Calculate isExperimentalCoroutineInReleaseEnvironment for type parameters + property.typeParameters.forEach { it.upperBounds } + + property.isExperimentalCoroutineInReleaseEnvironment = local.typeDeserializer.experimentalSuspendFunctionTypeEncountered && + c.components.configuration.releaseCoroutines && property.versionRequirement?.version != VersionRequirement.Version(1, 3) + val getter = if (hasGetter) { val getterFlags = proto.getterFlags val isNotDefault = proto.hasGetterFlags() && Flags.IS_NOT_DEFAULT.get(getterFlags) @@ -164,15 +155,13 @@ class MemberDeserializer(private val c: DeserializationContext) { ) val local = c.childContext(function, proto.typeParameterList) - val returnType = local.typeDeserializer.type(proto.returnType(c.typeTable)) - val valueParameters = local.memberDeserializer.valueParameters(proto.valueParameterList, proto, AnnotatedCallableKind.FUNCTION) - val continuationParameter = createContinuationParameterIfNeeded(function, returnType, valueParameters, flags) + val receiverType = proto.receiverType(c.typeTable)?.let { local.typeDeserializer.type(it, receiverAnnotations) } function.initialize( - proto.receiverType(c.typeTable)?.let { local.typeDeserializer.type(it, receiverAnnotations) }, + receiverType, getDispatchReceiverParameter(), local.typeDeserializer.ownTypeParameters, - valueParameters + listOfNotNull(continuationParameter), - if (continuationParameter == null) returnType else function.builtIns.nullableAnyType, + local.memberDeserializer.valueParameters(proto.valueParameterList, proto, AnnotatedCallableKind.FUNCTION), + local.typeDeserializer.type(proto.returnType(c.typeTable)), ProtoEnumFlags.modality(Flags.MODALITY.get(flags)), ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags)), emptyMap, Any?>() @@ -182,9 +171,16 @@ class MemberDeserializer(private val c: DeserializationContext) { function.isExternal = Flags.IS_EXTERNAL_FUNCTION.get(flags) function.isInline = Flags.IS_INLINE.get(flags) function.isTailrec = Flags.IS_TAILREC.get(flags) - function.isSuspend = Flags.IS_SUSPEND.get(flags) && loadAsSuspend(function.versionRequirement) + function.isSuspend = Flags.IS_SUSPEND.get(flags) function.isExpect = Flags.IS_EXPECT_FUNCTION.get(flags) + // Calculate isExperimentalCoroutineInReleaseEnvironment for type parameters + function.typeParameters.forEach { it.upperBounds } + + function.isExperimentalCoroutineInReleaseEnvironment = local.typeDeserializer.experimentalSuspendFunctionTypeEncountered || + (c.components.configuration.releaseCoroutines && Flags.IS_SUSPEND.get(flags) && + function.versionRequirement?.version != VersionRequirement.Version(1, 3)) + val mapValueForContract = c.components.contractDeserializer.deserializeContractFromFunction(proto, function, c.typeTable, c.typeDeserializer) if (mapValueForContract != null) { @@ -194,30 +190,6 @@ class MemberDeserializer(private val c: DeserializationContext) { return function } - private fun loadAsSuspend(versionRequirement: VersionRequirement?): Boolean = - if (c.components.configuration.releaseCoroutines) { - versionRequirement?.version == VersionRequirement.Version(1, 3) - } else true - - private fun createContinuationParameterIfNeeded( - functionDescriptor: DeserializedSimpleFunctionDescriptor, - returnType: KotlinType, - valueParameters: List, - flags: Int - ): ValueParameterDescriptor? = - if (!Flags.IS_SUSPEND.get(flags) || loadAsSuspend(functionDescriptor.versionRequirement)) null - else ValueParameterDescriptorImpl( - containingDeclaration = functionDescriptor, - original = null, - index = valueParameters.size, - annotations = Annotations.EMPTY, - name = Name.identifier("continuation"), - outType = functionDescriptor.module.getContinuationOfTypeOrAny(returnType, isReleaseCoroutines = false), - declaresDefaultValue = false, isCrossinline = false, - isNoinline = false, varargElementType = null, - source = SourceElement.NO_SOURCE - ) - fun loadTypeAlias(proto: ProtoBuf.TypeAlias): TypeAliasDescriptor { val annotations = AnnotationsImpl(proto.annotationList.map { annotationDeserializer.deserializeAnnotation(it, c.nameResolver) }) @@ -234,6 +206,11 @@ class MemberDeserializer(private val c: DeserializationContext) { local.typeDeserializer.simpleType(proto.expandedType(c.typeTable)) ) + // Calculate isExperimentalCoroutineInReleaseEnvironment for type parameters + local.typeDeserializer.ownTypeParameters.forEach { it.upperBounds } + + typeAlias.isExperimentalCoroutineInReleaseEnvironment = local.typeDeserializer.experimentalSuspendFunctionTypeEncountered + return typeAlias } @@ -254,6 +231,14 @@ class MemberDeserializer(private val c: DeserializationContext) { ProtoEnumFlags.visibility(Flags.VISIBILITY.get(proto.flags)) ) descriptor.returnType = classDescriptor.defaultType + + // Calculate isExperimentalCoroutineInReleaseEnvironment for type parameters + descriptor.typeParameters.forEach { it.upperBounds } + descriptor.isExperimentalCoroutineInReleaseEnvironment = local.typeDeserializer.experimentalSuspendFunctionTypeEncountered || + ((c.containingDeclaration as? DeserializedClassDescriptor)?.c?.typeDeserializer?.experimentalSuspendFunctionTypeEncountered == true && + c.components.configuration.releaseCoroutines && + descriptor.versionRequirement?.version != VersionRequirement.Version(1, 3)) + return descriptor } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt index 5ae05161b28..d42c223e74f 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/TypeDeserializer.kt @@ -5,9 +5,7 @@ package org.jetbrains.kotlin.serialization.deserialization -import org.jetbrains.kotlin.builtins.isFunctionType -import org.jetbrains.kotlin.builtins.isSuspendFunctionType -import org.jetbrains.kotlin.builtins.transformRuntimeFunctionTypeToSuspendFunction +import org.jetbrains.kotlin.builtins.* import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations @@ -25,7 +23,8 @@ class TypeDeserializer( private val c: DeserializationContext, private val parent: TypeDeserializer?, typeParameterProtos: List, - private val debugName: String + private val debugName: String, + var experimentalSuspendFunctionTypeEncountered: Boolean = false ) { private val classDescriptors: (Int) -> ClassDescriptor? = c.storageManager.createMemoizedFunctionWithNullableValues { fqNameIndex -> computeClassDescriptor(fqNameIndex) @@ -91,7 +90,7 @@ class TypeDeserializer( }.toList() val simpleType = if (Flags.SUSPEND_TYPE.get(proto.flags)) { - createSuspendFunctionType(annotations, constructor, arguments, proto.nullable, c.components.configuration.releaseCoroutines) + createSuspendFunctionType(annotations, constructor, arguments, proto.nullable) } else { KotlinTypeFactory.simpleType(annotations, constructor, arguments, proto.nullable) } @@ -131,11 +130,10 @@ class TypeDeserializer( annotations: Annotations, functionTypeConstructor: TypeConstructor, arguments: List, - nullable: Boolean, - isReleaseCoroutines: Boolean + nullable: Boolean ): SimpleType { val result = when (functionTypeConstructor.parameters.size - arguments.size) { - 0 -> createSuspendFunctionTypeForBasicCase(annotations, functionTypeConstructor, arguments, nullable, isReleaseCoroutines) + 0 -> createSuspendFunctionTypeForBasicCase(annotations, functionTypeConstructor, arguments, nullable) // This case for types written by eap compiler 1.1 1 -> { val arity = arguments.size - 1 @@ -162,8 +160,7 @@ class TypeDeserializer( annotations: Annotations, functionTypeConstructor: TypeConstructor, arguments: List, - nullable: Boolean, - isReleaseCoroutines: Boolean + nullable: Boolean ): SimpleType? { val functionType = KotlinTypeFactory.simpleType(annotations, functionTypeConstructor, arguments, nullable) if (!functionType.isFunctionType) return null @@ -171,16 +168,18 @@ class TypeDeserializer( // kotlin.suspend is still built with LV=1.2, thus it references old Continuation // And otherwise, once stdlib is compiled with 1.3 one may want to stay at LV=1.2 if (c.containingDeclaration.safeAs()?.fqNameOrNull() == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME) { - transformRuntimeFunctionTypeToSuspendFunction(functionType, false)?.let { oldSuspend -> - if (oldSuspend.isSuspendFunctionType) return oldSuspend - - transformRuntimeFunctionTypeToSuspendFunction(functionType, true)?.let { newSuspend -> return newSuspend } - } + val (suspendFun, experimental) = transformRuntimeFunctionTypeToSuspendFunction(functionType, true) + if (experimental) + transformRuntimeFunctionTypeToSuspendFunction(functionType, false).first?.let { return it } + else + return suspendFun } - transformRuntimeFunctionTypeToSuspendFunction(functionType, isReleaseCoroutines)?.let { return it } + val (type, experimental) = transformRuntimeFunctionTypeToSuspendFunction(functionType, c.components.configuration.releaseCoroutines) - return null + experimentalSuspendFunctionTypeEncountered = experimental || experimentalSuspendFunctionTypeEncountered + + return type } private fun typeParameterTypeConstructor(typeParameterId: Int): TypeConstructor? = diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt index 85ff2c7c44b..aef92247256 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedMemberDescriptor.kt @@ -34,6 +34,8 @@ interface DeserializedMemberDescriptor : MemberDescriptor { val typeTable: TypeTable + var isExperimentalCoroutineInReleaseEnvironment: Boolean + val versionRequirementTable: VersionRequirementTable val versionRequirement: VersionRequirement? @@ -68,7 +70,8 @@ class DeserializedSimpleFunctionDescriptor( override val typeTable: TypeTable, override val versionRequirementTable: VersionRequirementTable, override val containerSource: DeserializedContainerSource?, - source: SourceElement? = null + source: SourceElement? = null, + override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false ) : DeserializedCallableMemberDescriptor, SimpleFunctionDescriptorImpl( containingDeclaration, original, annotations, name, kind, @@ -85,7 +88,7 @@ class DeserializedSimpleFunctionDescriptor( ): FunctionDescriptorImpl { return DeserializedSimpleFunctionDescriptor( newOwner, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind, - proto, nameResolver, typeTable, versionRequirementTable, containerSource, source + proto, nameResolver, typeTable, versionRequirementTable, containerSource, source, isExperimentalCoroutineInReleaseEnvironment ) } } @@ -108,7 +111,8 @@ class DeserializedPropertyDescriptor( override val nameResolver: NameResolver, override val typeTable: TypeTable, override val versionRequirementTable: VersionRequirementTable, - override val containerSource: DeserializedContainerSource? + override val containerSource: DeserializedContainerSource?, + override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false ) : DeserializedCallableMemberDescriptor, PropertyDescriptorImpl( containingDeclaration, original, annotations, modality, visibility, isVar, name, kind, SourceElement.NO_SOURCE, isLateInit, isConst, isExpect, false, isExternal, isDelegated @@ -123,7 +127,8 @@ class DeserializedPropertyDescriptor( ): PropertyDescriptorImpl { return DeserializedPropertyDescriptor( newOwner, original, annotations, newModality, newVisibility, isVar, newName, kind, isLateInit, isConst, isExternal, - @Suppress("DEPRECATION") isDelegated, isExpect, proto, nameResolver, typeTable, versionRequirementTable, containerSource + @Suppress("DEPRECATION") isDelegated, isExpect, proto, nameResolver, typeTable, versionRequirementTable, containerSource, + isExperimentalCoroutineInReleaseEnvironment ) } @@ -141,7 +146,8 @@ class DeserializedClassConstructorDescriptor( override val typeTable: TypeTable, override val versionRequirementTable: VersionRequirementTable, override val containerSource: DeserializedContainerSource?, - source: SourceElement? = null + source: SourceElement? = null, + override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false ) : DeserializedCallableMemberDescriptor, ClassConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, source ?: SourceElement.NO_SOURCE) { @@ -178,7 +184,8 @@ class DeserializedTypeAliasDescriptor( override val nameResolver: NameResolver, override val typeTable: TypeTable, override val versionRequirementTable: VersionRequirementTable, - override val containerSource: DeserializedContainerSource? + override val containerSource: DeserializedContainerSource?, + override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false ) : AbstractTypeAliasDescriptor(containingDeclaration, annotations, name, SourceElement.NO_SOURCE, visibility), DeserializedMemberDescriptor { override lateinit var constructors: Collection private set @@ -210,7 +217,8 @@ class DeserializedTypeAliasDescriptor( if (substitutor.isEmpty) return this val substituted = DeserializedTypeAliasDescriptor( storageManager, containingDeclaration, annotations, name, visibility, - proto, nameResolver, typeTable, versionRequirementTable, containerSource + proto, nameResolver, typeTable, versionRequirementTable, containerSource, + isExperimentalCoroutineInReleaseEnvironment ) substituted.initialize( declaredTypeParameters,