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:
Denis Zharkov
2018-08-20 11:30:32 +03:00
parent c94b72680a
commit 944b0d058a
13 changed files with 272 additions and 89 deletions
@@ -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,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