Allow calling some pieces of the experimental coroutine API
- Calling suspend functions is allowed - Presence of suspend function type still makes declaration unusable unless it belongs to a value parameter as a top-level type containing less then three parameters Still, warning should be emitted because they will become unsupported in 1.4 #KT-25683 In Progress
This commit is contained in:
+109
-12
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.serialization.deserialization
|
||||
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
|
||||
@@ -17,7 +18,12 @@ import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.*
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.*
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
||||
|
||||
class MemberDeserializer(private val c: DeserializationContext) {
|
||||
private val annotationDeserializer = AnnotationDeserializer(c.components.moduleDescriptor, c.components.notFoundClasses)
|
||||
@@ -130,12 +136,97 @@ class MemberDeserializer(private val c: DeserializationContext) {
|
||||
return property
|
||||
}
|
||||
|
||||
private fun DeserializedMemberDescriptor.checkExperimentalCoroutine(typeDeserializer: TypeDeserializer): Boolean {
|
||||
// Calculate checkExperimentalCoroutine for type parameters
|
||||
typeDeserializer.ownTypeParameters.forEach { it.upperBounds }
|
||||
return typeDeserializer.experimentalSuspendFunctionTypeEncountered && versionAndReleaseCoroutinesMismatch()
|
||||
private fun DeserializedMemberDescriptor.checkExperimentalCoroutine(
|
||||
typeDeserializer: TypeDeserializer
|
||||
): DeserializedMemberDescriptor.CoroutinesCompatibilityMode {
|
||||
if (!versionAndReleaseCoroutinesMismatch()) return CoroutinesCompatibilityMode.COMPATIBLE
|
||||
|
||||
forceUpperBoundsComputation(typeDeserializer)
|
||||
|
||||
return if (typeDeserializer.experimentalSuspendFunctionTypeEncountered)
|
||||
CoroutinesCompatibilityMode.INCOMPATIBLE
|
||||
else
|
||||
CoroutinesCompatibilityMode.COMPATIBLE
|
||||
}
|
||||
|
||||
private fun forceUpperBoundsComputation(typeDeserializer: TypeDeserializer) {
|
||||
typeDeserializer.ownTypeParameters.forEach { it.upperBounds }
|
||||
}
|
||||
|
||||
private fun DeserializedSimpleFunctionDescriptor.initializeWithCoroutinesExperimentalityStatus(
|
||||
receiverParameterType: KotlinType?,
|
||||
dispatchReceiverParameter: ReceiverParameterDescriptor?,
|
||||
typeParameters: List<TypeParameterDescriptor>,
|
||||
unsubstitutedValueParameters: List<ValueParameterDescriptor>,
|
||||
unsubstitutedReturnType: KotlinType?,
|
||||
modality: Modality?,
|
||||
visibility: Visibility,
|
||||
userDataMap: Map<out FunctionDescriptor.UserDataKey<*>, *>,
|
||||
isSuspend: Boolean
|
||||
) {
|
||||
initialize(
|
||||
receiverParameterType,
|
||||
dispatchReceiverParameter,
|
||||
typeParameters,
|
||||
unsubstitutedValueParameters,
|
||||
unsubstitutedReturnType,
|
||||
modality,
|
||||
visibility,
|
||||
userDataMap,
|
||||
computeExperimentalityModeForFunctions(
|
||||
receiverParameterType,
|
||||
unsubstitutedValueParameters,
|
||||
typeParameters,
|
||||
unsubstitutedReturnType,
|
||||
isSuspend
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun DeserializedCallableMemberDescriptor.computeExperimentalityModeForFunctions(
|
||||
extensionReceiverType: KotlinType?,
|
||||
parameters: Collection<ValueParameterDescriptor>,
|
||||
typeParameters: Collection<TypeParameterDescriptor>,
|
||||
returnType: KotlinType?,
|
||||
isSuspend: Boolean
|
||||
): DeserializedMemberDescriptor.CoroutinesCompatibilityMode {
|
||||
if (!versionAndReleaseCoroutinesMismatch()) return CoroutinesCompatibilityMode.COMPATIBLE
|
||||
if (fqNameOrNull() == KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME) return CoroutinesCompatibilityMode.COMPATIBLE
|
||||
|
||||
val types = parameters.map { it.type } + listOfNotNull(extensionReceiverType)
|
||||
|
||||
if (returnType?.containsSuspendFunctionType() == true) return CoroutinesCompatibilityMode.INCOMPATIBLE
|
||||
if (typeParameters.any { typeParameter -> typeParameter.upperBounds.any { it.containsSuspendFunctionType() } }) {
|
||||
return CoroutinesCompatibilityMode.INCOMPATIBLE
|
||||
}
|
||||
|
||||
val maxFromParameters = types.map { type ->
|
||||
when {
|
||||
type.isSuspendFunctionType && type.arguments.size <= 3 ->
|
||||
if (type.arguments.any { it.type.containsSuspendFunctionType() })
|
||||
CoroutinesCompatibilityMode.INCOMPATIBLE
|
||||
else
|
||||
CoroutinesCompatibilityMode.NEEDS_WRAPPER
|
||||
|
||||
type.containsSuspendFunctionType() -> CoroutinesCompatibilityMode.INCOMPATIBLE
|
||||
|
||||
else -> CoroutinesCompatibilityMode.COMPATIBLE
|
||||
}
|
||||
}.max() ?: CoroutinesCompatibilityMode.COMPATIBLE
|
||||
|
||||
return maxOf(
|
||||
if (isSuspend)
|
||||
CoroutinesCompatibilityMode.NEEDS_WRAPPER
|
||||
else
|
||||
CoroutinesCompatibilityMode.COMPATIBLE,
|
||||
maxFromParameters
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private fun KotlinType.containsSuspendFunctionType() = contains(UnwrappedType::isSuspendFunctionType)
|
||||
|
||||
|
||||
private fun DeserializedMemberDescriptor.versionAndReleaseCoroutinesMismatch(): Boolean =
|
||||
c.components.configuration.releaseCoroutines && versionRequirements.none {
|
||||
it.version == VersionRequirement.Version(1, 3) && it.kind == ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION
|
||||
@@ -160,7 +251,7 @@ class MemberDeserializer(private val c: DeserializationContext) {
|
||||
)
|
||||
val local = c.childContext(function, proto.typeParameterList)
|
||||
|
||||
function.initialize(
|
||||
function.initializeWithCoroutinesExperimentalityStatus(
|
||||
proto.receiverType(c.typeTable)?.let { local.typeDeserializer.type(it, receiverAnnotations) },
|
||||
getDispatchReceiverParameter(),
|
||||
local.typeDeserializer.ownTypeParameters,
|
||||
@@ -169,8 +260,7 @@ class MemberDeserializer(private val c: DeserializationContext) {
|
||||
ProtoEnumFlags.modality(Flags.MODALITY.get(flags)),
|
||||
ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags)),
|
||||
emptyMap<FunctionDescriptor.UserDataKey<*>, Any?>(),
|
||||
(Flags.IS_SUSPEND.get(flags) && function.versionAndReleaseCoroutinesMismatch()) ||
|
||||
function.checkExperimentalCoroutine(local.typeDeserializer)
|
||||
Flags.IS_SUSPEND.get(flags)
|
||||
)
|
||||
function.isOperator = Flags.IS_OPERATOR.get(flags)
|
||||
function.isInfix = Flags.IS_INFIX.get(flags)
|
||||
@@ -227,11 +317,18 @@ class MemberDeserializer(private val c: DeserializationContext) {
|
||||
)
|
||||
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 &&
|
||||
descriptor.versionAndReleaseCoroutinesMismatch())
|
||||
val doesClassContainIncompatibility =
|
||||
(c.containingDeclaration as? DeserializedClassDescriptor)
|
||||
?.c?.typeDeserializer?.experimentalSuspendFunctionTypeEncountered == true
|
||||
&& descriptor.versionAndReleaseCoroutinesMismatch()
|
||||
|
||||
descriptor.coroutinesExperimentalCompatibilityMode =
|
||||
if (doesClassContainIncompatibility)
|
||||
CoroutinesCompatibilityMode.INCOMPATIBLE
|
||||
else descriptor.computeExperimentalityModeForFunctions(
|
||||
null, descriptor.valueParameters, descriptor.typeParameters,
|
||||
descriptor.returnType, isSuspend = false
|
||||
)
|
||||
|
||||
return descriptor
|
||||
}
|
||||
|
||||
+23
-15
@@ -23,8 +23,6 @@ interface DeserializedMemberDescriptor : MemberDescriptor {
|
||||
|
||||
val typeTable: TypeTable
|
||||
|
||||
val isExperimentalCoroutineInReleaseEnvironment: Boolean
|
||||
|
||||
val versionRequirementTable: VersionRequirementTable
|
||||
|
||||
val versionRequirements: List<VersionRequirement>
|
||||
@@ -33,6 +31,14 @@ interface DeserializedMemberDescriptor : MemberDescriptor {
|
||||
// Information about the origin of this callable's container (class or package part on JVM) or null if there's no such information.
|
||||
// TODO: merge with sourceElement of containingDeclaration
|
||||
val containerSource: DeserializedContainerSource?
|
||||
|
||||
val coroutinesExperimentalCompatibilityMode: CoroutinesCompatibilityMode
|
||||
|
||||
enum class CoroutinesCompatibilityMode {
|
||||
COMPATIBLE,
|
||||
NEEDS_WRAPPER,
|
||||
INCOMPATIBLE
|
||||
}
|
||||
}
|
||||
|
||||
interface DeserializedContainerSource : SourceElement {
|
||||
@@ -66,7 +72,7 @@ class DeserializedSimpleFunctionDescriptor(
|
||||
source ?: SourceElement.NO_SOURCE
|
||||
) {
|
||||
|
||||
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false
|
||||
override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
|
||||
private set
|
||||
|
||||
fun initialize(
|
||||
@@ -78,7 +84,7 @@ class DeserializedSimpleFunctionDescriptor(
|
||||
modality: Modality?,
|
||||
visibility: Visibility,
|
||||
userDataMap: Map<out FunctionDescriptor.UserDataKey<*>, *>,
|
||||
isExperimentalCoroutineInReleaseEnvironment: Boolean
|
||||
isExperimentalCoroutineInReleaseEnvironment: DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
||||
): SimpleFunctionDescriptorImpl {
|
||||
return super.initialize(
|
||||
receiverParameterType,
|
||||
@@ -90,7 +96,7 @@ class DeserializedSimpleFunctionDescriptor(
|
||||
visibility,
|
||||
userDataMap
|
||||
).also {
|
||||
this.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment
|
||||
this.coroutinesExperimentalCompatibilityMode = isExperimentalCoroutineInReleaseEnvironment
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +111,9 @@ class DeserializedSimpleFunctionDescriptor(
|
||||
return DeserializedSimpleFunctionDescriptor(
|
||||
newOwner, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind,
|
||||
proto, nameResolver, typeTable, versionRequirementTable, containerSource, source
|
||||
)
|
||||
).also {
|
||||
it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,16 +140,16 @@ class DeserializedPropertyDescriptor(
|
||||
containingDeclaration, original, annotations, modality, visibility, isVar, name, kind, SourceElement.NO_SOURCE,
|
||||
isLateInit, isConst, isExpect, false, isExternal, isDelegated
|
||||
) {
|
||||
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false
|
||||
override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
|
||||
private set
|
||||
|
||||
fun initialize(
|
||||
getter: PropertyGetterDescriptorImpl?,
|
||||
setter: PropertySetterDescriptor?,
|
||||
isExperimentalCoroutineInReleaseEnvironment: Boolean
|
||||
isExperimentalCoroutineInReleaseEnvironment: DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
||||
) {
|
||||
super.initialize(getter, setter)
|
||||
.also { this.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment }
|
||||
.also { this.coroutinesExperimentalCompatibilityMode = isExperimentalCoroutineInReleaseEnvironment }
|
||||
}
|
||||
|
||||
override fun createSubstitutedCopy(
|
||||
@@ -176,7 +184,7 @@ class DeserializedClassConstructorDescriptor(
|
||||
) : DeserializedCallableMemberDescriptor,
|
||||
ClassConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, source ?: SourceElement.NO_SOURCE) {
|
||||
|
||||
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false
|
||||
override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
|
||||
internal set
|
||||
|
||||
override fun createSubstitutedCopy(
|
||||
@@ -190,7 +198,7 @@ class DeserializedClassConstructorDescriptor(
|
||||
return DeserializedClassConstructorDescriptor(
|
||||
newOwner as ClassDescriptor, original as ConstructorDescriptor?, annotations, isPrimary, kind,
|
||||
proto, nameResolver, typeTable, versionRequirementTable, containerSource, source
|
||||
).also { it.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment }
|
||||
).also { it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode }
|
||||
}
|
||||
|
||||
override fun isExternal(): Boolean = false
|
||||
@@ -222,14 +230,14 @@ class DeserializedTypeAliasDescriptor(
|
||||
private lateinit var typeConstructorParameters: List<TypeParameterDescriptor>
|
||||
private lateinit var defaultTypeImpl: SimpleType
|
||||
|
||||
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false
|
||||
override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
|
||||
private set
|
||||
|
||||
fun initialize(
|
||||
declaredTypeParameters: List<TypeParameterDescriptor>,
|
||||
underlyingType: SimpleType,
|
||||
expandedType: SimpleType,
|
||||
isExperimentalCoroutineInReleaseEnvironment: Boolean
|
||||
isExperimentalCoroutineInReleaseEnvironment: DeserializedMemberDescriptor.CoroutinesCompatibilityMode
|
||||
) {
|
||||
initialize(declaredTypeParameters)
|
||||
this.underlyingType = underlyingType
|
||||
@@ -237,7 +245,7 @@ class DeserializedTypeAliasDescriptor(
|
||||
typeConstructorParameters = computeConstructorTypeParameters()
|
||||
defaultTypeImpl = computeDefaultType()
|
||||
constructors = getTypeAliasConstructors()
|
||||
this.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment
|
||||
this.coroutinesExperimentalCompatibilityMode = isExperimentalCoroutineInReleaseEnvironment
|
||||
}
|
||||
|
||||
override val classDescriptor: ClassDescriptor?
|
||||
@@ -255,7 +263,7 @@ class DeserializedTypeAliasDescriptor(
|
||||
declaredTypeParameters,
|
||||
substitutor.safeSubstitute(underlyingType, Variance.INVARIANT).asSimpleType(),
|
||||
substitutor.safeSubstitute(expandedType, Variance.INVARIANT).asSimpleType(),
|
||||
isExperimentalCoroutineInReleaseEnvironment
|
||||
coroutinesExperimentalCompatibilityMode
|
||||
)
|
||||
|
||||
return substituted
|
||||
|
||||
Reference in New Issue
Block a user