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
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor.CoroutinesCompatibilityMode.*
import org.jetbrains.kotlin.storage.StorageManager import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
@@ -104,10 +105,14 @@ private data class DeprecatedByOverridden(private val deprecations: Collection<D
} }
private data class DeprecatedExperimentalCoroutine( private data class DeprecatedExperimentalCoroutine(
override val target: DeclarationDescriptor override val target: DeclarationDescriptor,
override val deprecationLevel: DeprecationLevelValue
) : Deprecation { ) : Deprecation {
override val deprecationLevel: DeprecationLevelValue = DeprecationLevelValue.ERROR override val message: String? =
override val message: String? = "Experimental coroutine cannot be used with API version 1.3" if (deprecationLevel == WARNING)
"Experimental coroutines support will be dropped in 1.4"
else
"Experimental coroutine cannot be used with API version 1.3"
} }
private data class DeprecatedByVersionRequirement( private data class DeprecatedByVersionRequirement(
@@ -300,7 +305,7 @@ class DeprecationResolver(
fun addDeprecationIfPresent(target: DeclarationDescriptor) { fun addDeprecationIfPresent(target: DeclarationDescriptor) {
val annotation = target.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated) val annotation = target.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.deprecated)
?: target.annotations.findAnnotation(JAVA_DEPRECATED) ?: target.annotations.findAnnotation(JAVA_DEPRECATED)
if (annotation != null) { if (annotation != null) {
val deprecatedByAnnotation = DeprecatedByAnnotation(annotation, target) val deprecatedByAnnotation = DeprecatedByAnnotation(annotation, target)
val deprecation = when (target) { val deprecation = when (target) {
@@ -323,7 +328,7 @@ class DeprecationResolver(
useSiteTarget, useSiteTarget,
KotlinBuiltIns.FQ_NAMES.deprecated KotlinBuiltIns.FQ_NAMES.deprecated
) )
?: Annotations.findUseSiteTargetedAnnotation(annotatedDescriptor.annotations, useSiteTarget, JAVA_DEPRECATED) ?: Annotations.findUseSiteTargetedAnnotation(annotatedDescriptor.annotations, useSiteTarget, JAVA_DEPRECATED)
if (annotation != null) { if (annotation != null) {
result.add(DeprecatedByAnnotation(annotation, this)) result.add(DeprecatedByAnnotation(annotation, this))
} }
@@ -353,10 +358,14 @@ class DeprecationResolver(
return result.distinct() return result.distinct()
} }
private fun getDeprecationByCoroutinesVersion(target: DeclarationDescriptor): DeprecatedExperimentalCoroutine? = private fun getDeprecationByCoroutinesVersion(target: DeclarationDescriptor): DeprecatedExperimentalCoroutine? {
if (target is DeserializedMemberDescriptor && target.isExperimentalCoroutineInReleaseEnvironment) if (target !is DeserializedMemberDescriptor) return null
DeprecatedExperimentalCoroutine(target) return when (target.coroutinesExperimentalCompatibilityMode) {
else null COMPATIBLE -> null
NEEDS_WRAPPER -> DeprecatedExperimentalCoroutine(target, WARNING)
INCOMPATIBLE -> DeprecatedExperimentalCoroutine(target, ERROR)
}
}
private fun getDeprecationByVersionRequirement(target: DeclarationDescriptor): List<DeprecatedByVersionRequirement> { private fun getDeprecationByVersionRequirement(target: DeclarationDescriptor): List<DeprecatedByVersionRequirement> {
fun createVersion(version: String): MavenComparableVersion? = try { fun createVersion(version: String): MavenComparableVersion? = try {
@@ -1,27 +1,14 @@
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 = {} val c: suspend () -> Unit = {}
class WithTypeParameter<T: suspend() -> Unit> {} class WithTypeParameter<T: suspend() -> Unit> {}
fun returnsSuspend() : suspend() -> Unit = {} fun returnsSuspend() : suspend() -> Unit = {}
fun builder(c: suspend () -> Unit) {}
fun <T: suspend () -> Unit> withTypeParameter() = {} fun <T: suspend () -> Unit> withTypeParameter() = {}
fun suspendFunctionNested(x: List<suspend () -> Unit>) {}
fun suspendFunctionNestedInFunctionType(x: (suspend () -> Unit) -> Unit) {}
fun suspendFunctionType3(x: suspend (Int, Int, Int) -> Unit) {}
fun suspendVarargs(vararg x: suspend () -> Unit) {}
@@ -1,29 +1,30 @@
warning: language version 1.3 is experimental, there are no backwards compatibility guarantees for new language and library features 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 warning: runtime JAR files in the classpath should have the same version. These files were found in the classpath:
c() $DIST_DIR$/kotlin-stdlib-coroutines.jar (version 1.3)
^ $PROJECT_DIR$/lib/kotlin-stdlib.jar (version 1.2)
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:3:5: error: using 'constructor WithTypeParameter<T : suspend () -> Unit>()' is an error. Experimental coroutine cannot be used with API version 1.3 $PROJECT_DIR$/lib/kotlin-script-runtime.jar (version 1.2)
$PROJECT_DIR$/lib/kotlin-reflect.jar (version 1.2)
warning: consider providing an explicit dependency on kotlin-reflect 1.3 to prevent strange errors
warning: some runtime JAR files in the classpath have an incompatible version. Consider removing them from the classpath
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:2:5: error: using 'constructor WithTypeParameter<T : suspend () -> Unit>()' is an error. Experimental coroutine cannot be used with API version 1.3
WithTypeParameter<suspend () -> Unit>() WithTypeParameter<suspend () -> 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 compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:3:5: error: using 'returnsSuspend(): suspend () -> Unit' is an error. Experimental coroutine cannot be used with API version 1.3
returnsSuspend() 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 compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:4:5: error: using 'withTypeParameter(): () -> 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<suspend () -> Unit>() withTypeParameter<suspend () -> 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 compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:6:5: error: using 'suspendFunctionNested(List<suspend () -> Unit>): Unit' is an error. Experimental coroutine cannot be used with API version 1.3
dummy() suspendFunctionNested(listOf(suspend { }))
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:7:5: error: using 'suspendFunctionNestedInFunctionType((suspend () -> Unit) -> Unit): Unit' is an error. Experimental coroutine cannot be used with API version 1.3
suspendFunctionNestedInFunctionType {}
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:8:5: error: using 'suspendFunctionType3(suspend (Int, Int, Int) -> Unit): Unit' is an error. Experimental coroutine cannot be used with API version 1.3
suspendFunctionType3 { x, y, z -> }
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromRelease/release.kt:10:5: error: using 'suspendVarargs(vararg suspend () -> Unit): Unit' is an error. Experimental coroutine cannot be used with API version 1.3
suspendVarargs({}, {})
^ ^
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 COMPILATION_ERROR
@@ -1,12 +1,11 @@
suspend fun callRelease() { suspend fun callRelease() {
c()
WithTypeParameter<suspend () -> Unit>() WithTypeParameter<suspend () -> Unit>()
returnsSuspend() returnsSuspend()
builder {}
withTypeParameter<suspend () -> Unit>() withTypeParameter<suspend () -> Unit>()
dummy() suspendFunctionNested(listOf(suspend { }))
C().dummy() suspendFunctionNestedInFunctionType {}
WithNested.Nested().dummy() suspendFunctionType3 { x, y, z -> }
WithInner().Inner().dummy()
suspendVarargs({}, {})
} }
@@ -0,0 +1,24 @@
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"
}
}
fun builder(c: suspend () -> Unit) {}
fun builder2(c: suspend Int.(String) -> Unit) {}
fun (suspend (Int) -> Unit).start() {}
suspend fun suspendAcceptsSuspend(x: suspend () -> Unit) {}
@@ -0,0 +1,33 @@
warning: language version 1.3 is experimental, there are no backwards compatibility guarantees for new language and library features
warning: runtime JAR files in the classpath should have the same version. These files were found in the classpath:
$DIST_DIR$/kotlin-stdlib-coroutines.jar (version 1.3)
$PROJECT_DIR$/lib/kotlin-stdlib.jar (version 1.2)
$PROJECT_DIR$/lib/kotlin-script-runtime.jar (version 1.2)
$PROJECT_DIR$/lib/kotlin-reflect.jar (version 1.2)
warning: consider providing an explicit dependency on kotlin-reflect 1.3 to prevent strange errors
warning: some runtime JAR files in the classpath have an incompatible version. Consider removing them from the classpath
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:2:5: warning: 'builder(suspend () -> Unit): Unit' is deprecated. Experimental coroutines support will be dropped in 1.4
builder {}
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:3:5: warning: 'builder2(suspend Int.(String) -> Unit): Unit' is deprecated. Experimental coroutines support will be dropped in 1.4
builder2 { }
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:6:7: warning: 'start(): Unit' is deprecated. Experimental coroutines support will be dropped in 1.4
x.start()
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:8:5: warning: 'dummy(): Unit' is deprecated. Experimental coroutines support will be dropped in 1.4
dummy()
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:9:9: warning: 'dummy(): String' is deprecated. Experimental coroutines support will be dropped in 1.4
C().dummy()
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:10:25: warning: 'dummy(): String' is deprecated. Experimental coroutines support will be dropped in 1.4
WithNested.Nested().dummy()
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:11:25: warning: 'dummy(): String' is deprecated. Experimental coroutines support will be dropped in 1.4
WithInner().Inner().dummy()
^
compiler/testData/compileKotlinAgainstCustomBinaries/experimentalCoroutineCallFromReleaseWarnings/release.kt:13:5: warning: 'suspendAcceptsSuspend(suspend () -> Unit): Unit' is deprecated. Experimental coroutines support will be dropped in 1.4
suspendAcceptsSuspend {
^
OK
@@ -0,0 +1,16 @@
suspend fun callRelease() {
builder {}
builder2 { }
val x: suspend (Int) -> Unit = {}
x.start()
dummy()
C().dummy()
WithNested.Nested().dummy()
WithInner().Inner().dummy()
suspendAcceptsSuspend {
callRelease()
}
}
@@ -24,14 +24,14 @@ fun builder(c: <!UNSUPPORTED!>suspend<!> () -> Unit) {}
} }
fun test2() { fun test2() {
kotlin.coroutines.experimental.<!DEPRECATION_ERROR, UNSUPPORTED!>buildSequence<!><Int> { kotlin.coroutines.experimental.<!DEPRECATION, UNSUPPORTED!>buildSequence<!><Int> {
<!DEPRECATION_ERROR!>yield<!>(1) <!DEPRECATION!>yield<!>(1)
} }
kotlin.sequences.<!UNRESOLVED_REFERENCE!>buildSequence<!><Int> { kotlin.sequences.<!UNRESOLVED_REFERENCE!>buildSequence<!><Int> {
<!UNRESOLVED_REFERENCE!>yield<!>(1) <!UNRESOLVED_REFERENCE!>yield<!>(1)
} }
} }
<!UNSUPPORTED!>suspend<!> fun test3(): Unit = kotlin.coroutines.experimental.<!DEPRECATION_ERROR!>suspendCoroutine<!> { _ -> Unit } <!UNSUPPORTED!>suspend<!> fun test3(): Unit = kotlin.coroutines.experimental.<!DEPRECATION!>suspendCoroutine<!> { _ -> Unit }
<!UNSUPPORTED!>suspend<!> fun test4(): Unit = kotlin.coroutines.<!UNRESOLVED_REFERENCE!>suspendCoroutine<!> { <!CANNOT_INFER_PARAMETER_TYPE!>_<!> -> Unit } <!UNSUPPORTED!>suspend<!> fun test4(): Unit = kotlin.coroutines.<!UNRESOLVED_REFERENCE!>suspendCoroutine<!> { <!CANNOT_INFER_PARAMETER_TYPE!>_<!> -> Unit }
@@ -79,6 +79,7 @@ public abstract class AbstractCliTest extends TestCaseWithTmpdir {
.replace(testDataAbsoluteDir, TESTDATA_DIR) .replace(testDataAbsoluteDir, TESTDATA_DIR)
.replace(FileUtil.toSystemIndependentName(testDataAbsoluteDir), TESTDATA_DIR) .replace(FileUtil.toSystemIndependentName(testDataAbsoluteDir), TESTDATA_DIR)
.replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getAbsolutePath(), "$PROJECT_DIR$") .replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getAbsolutePath(), "$PROJECT_DIR$")
.replace(PathUtil.getKotlinPathsForDistDirectory().getHomePath().getParentFile().getAbsolutePath(), "$DIST_DIR$")
.replace("expected version is " + JvmMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$") .replace("expected version is " + JvmMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$")
.replace("expected version is " + JsMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$") .replace("expected version is " + JsMetadataVersion.INSTANCE, "expected version is $ABI_VERSION$")
.replace("\\", "/") .replace("\\", "/")
@@ -484,6 +484,14 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
} }
fun testExperimentalCoroutineCallFromRelease() { fun testExperimentalCoroutineCallFromRelease() {
doTestExperimentalCoroutineCallFromRelease()
}
fun testExperimentalCoroutineCallFromReleaseWarnings() {
doTestExperimentalCoroutineCallFromRelease()
}
private fun doTestExperimentalCoroutineCallFromRelease() {
val library = compileLibrary( val library = compileLibrary(
"library", "library",
additionalOptions = listOf("-language-version", "1.2"), additionalOptions = listOf("-language-version", "1.2"),
@@ -492,7 +500,7 @@ class CompileKotlinAgainstCustomBinariesTest : AbstractKotlinCompilerIntegration
compileKotlin( compileKotlin(
"release.kt", "release.kt",
tmpdir, tmpdir,
listOf(library), listOf(library, ForTestCompileRuntime.coroutinesJarForTests()),
additionalOptions = listOf("-language-version", "1.3", "-api-version", "1.3") additionalOptions = listOf("-language-version", "1.3", "-api-version", "1.3")
) )
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.serialization.deserialization package org.jetbrains.kotlin.serialization.deserialization
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget 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.metadata.deserialization.*
import org.jetbrains.kotlin.protobuf.MessageLite import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.resolve.DescriptorFactory 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.*
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) { class MemberDeserializer(private val c: DeserializationContext) {
private val annotationDeserializer = AnnotationDeserializer(c.components.moduleDescriptor, c.components.notFoundClasses) private val annotationDeserializer = AnnotationDeserializer(c.components.moduleDescriptor, c.components.notFoundClasses)
@@ -130,12 +136,97 @@ class MemberDeserializer(private val c: DeserializationContext) {
return property return property
} }
private fun DeserializedMemberDescriptor.checkExperimentalCoroutine(typeDeserializer: TypeDeserializer): Boolean { private fun DeserializedMemberDescriptor.checkExperimentalCoroutine(
// Calculate checkExperimentalCoroutine for type parameters typeDeserializer: TypeDeserializer
typeDeserializer.ownTypeParameters.forEach { it.upperBounds } ): DeserializedMemberDescriptor.CoroutinesCompatibilityMode {
return typeDeserializer.experimentalSuspendFunctionTypeEncountered && versionAndReleaseCoroutinesMismatch() 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 = private fun DeserializedMemberDescriptor.versionAndReleaseCoroutinesMismatch(): Boolean =
c.components.configuration.releaseCoroutines && versionRequirements.none { c.components.configuration.releaseCoroutines && versionRequirements.none {
it.version == VersionRequirement.Version(1, 3) && it.kind == ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION 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) val local = c.childContext(function, proto.typeParameterList)
function.initialize( function.initializeWithCoroutinesExperimentalityStatus(
proto.receiverType(c.typeTable)?.let { local.typeDeserializer.type(it, receiverAnnotations) }, proto.receiverType(c.typeTable)?.let { local.typeDeserializer.type(it, receiverAnnotations) },
getDispatchReceiverParameter(), getDispatchReceiverParameter(),
local.typeDeserializer.ownTypeParameters, local.typeDeserializer.ownTypeParameters,
@@ -169,8 +260,7 @@ class MemberDeserializer(private val c: DeserializationContext) {
ProtoEnumFlags.modality(Flags.MODALITY.get(flags)), ProtoEnumFlags.modality(Flags.MODALITY.get(flags)),
ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags)), ProtoEnumFlags.visibility(Flags.VISIBILITY.get(flags)),
emptyMap<FunctionDescriptor.UserDataKey<*>, Any?>(), emptyMap<FunctionDescriptor.UserDataKey<*>, Any?>(),
(Flags.IS_SUSPEND.get(flags) && function.versionAndReleaseCoroutinesMismatch()) || Flags.IS_SUSPEND.get(flags)
function.checkExperimentalCoroutine(local.typeDeserializer)
) )
function.isOperator = Flags.IS_OPERATOR.get(flags) function.isOperator = Flags.IS_OPERATOR.get(flags)
function.isInfix = Flags.IS_INFIX.get(flags) function.isInfix = Flags.IS_INFIX.get(flags)
@@ -227,11 +317,18 @@ class MemberDeserializer(private val c: DeserializationContext) {
) )
descriptor.returnType = classDescriptor.defaultType descriptor.returnType = classDescriptor.defaultType
// Calculate isExperimentalCoroutineInReleaseEnvironment for type parameters val doesClassContainIncompatibility =
descriptor.typeParameters.forEach { it.upperBounds } (c.containingDeclaration as? DeserializedClassDescriptor)
descriptor.isExperimentalCoroutineInReleaseEnvironment = local.typeDeserializer.experimentalSuspendFunctionTypeEncountered || ?.c?.typeDeserializer?.experimentalSuspendFunctionTypeEncountered == true
((c.containingDeclaration as? DeserializedClassDescriptor)?.c?.typeDeserializer?.experimentalSuspendFunctionTypeEncountered == true && && descriptor.versionAndReleaseCoroutinesMismatch()
descriptor.versionAndReleaseCoroutinesMismatch())
descriptor.coroutinesExperimentalCompatibilityMode =
if (doesClassContainIncompatibility)
CoroutinesCompatibilityMode.INCOMPATIBLE
else descriptor.computeExperimentalityModeForFunctions(
null, descriptor.valueParameters, descriptor.typeParameters,
descriptor.returnType, isSuspend = false
)
return descriptor return descriptor
} }
@@ -23,8 +23,6 @@ interface DeserializedMemberDescriptor : MemberDescriptor {
val typeTable: TypeTable val typeTable: TypeTable
val isExperimentalCoroutineInReleaseEnvironment: Boolean
val versionRequirementTable: VersionRequirementTable val versionRequirementTable: VersionRequirementTable
val versionRequirements: List<VersionRequirement> 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. // 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 // TODO: merge with sourceElement of containingDeclaration
val containerSource: DeserializedContainerSource? val containerSource: DeserializedContainerSource?
val coroutinesExperimentalCompatibilityMode: CoroutinesCompatibilityMode
enum class CoroutinesCompatibilityMode {
COMPATIBLE,
NEEDS_WRAPPER,
INCOMPATIBLE
}
} }
interface DeserializedContainerSource : SourceElement { interface DeserializedContainerSource : SourceElement {
@@ -66,7 +72,7 @@ class DeserializedSimpleFunctionDescriptor(
source ?: SourceElement.NO_SOURCE source ?: SourceElement.NO_SOURCE
) { ) {
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
private set private set
fun initialize( fun initialize(
@@ -78,7 +84,7 @@ class DeserializedSimpleFunctionDescriptor(
modality: Modality?, modality: Modality?,
visibility: Visibility, visibility: Visibility,
userDataMap: Map<out FunctionDescriptor.UserDataKey<*>, *>, userDataMap: Map<out FunctionDescriptor.UserDataKey<*>, *>,
isExperimentalCoroutineInReleaseEnvironment: Boolean isExperimentalCoroutineInReleaseEnvironment: DeserializedMemberDescriptor.CoroutinesCompatibilityMode
): SimpleFunctionDescriptorImpl { ): SimpleFunctionDescriptorImpl {
return super.initialize( return super.initialize(
receiverParameterType, receiverParameterType,
@@ -90,7 +96,7 @@ class DeserializedSimpleFunctionDescriptor(
visibility, visibility,
userDataMap userDataMap
).also { ).also {
this.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment this.coroutinesExperimentalCompatibilityMode = isExperimentalCoroutineInReleaseEnvironment
} }
} }
@@ -105,7 +111,9 @@ class DeserializedSimpleFunctionDescriptor(
return DeserializedSimpleFunctionDescriptor( return DeserializedSimpleFunctionDescriptor(
newOwner, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind, newOwner, original as SimpleFunctionDescriptor?, annotations, newName ?: name, kind,
proto, nameResolver, typeTable, versionRequirementTable, containerSource, source 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, containingDeclaration, original, annotations, modality, visibility, isVar, name, kind, SourceElement.NO_SOURCE,
isLateInit, isConst, isExpect, false, isExternal, isDelegated isLateInit, isConst, isExpect, false, isExternal, isDelegated
) { ) {
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
private set private set
fun initialize( fun initialize(
getter: PropertyGetterDescriptorImpl?, getter: PropertyGetterDescriptorImpl?,
setter: PropertySetterDescriptor?, setter: PropertySetterDescriptor?,
isExperimentalCoroutineInReleaseEnvironment: Boolean isExperimentalCoroutineInReleaseEnvironment: DeserializedMemberDescriptor.CoroutinesCompatibilityMode
) { ) {
super.initialize(getter, setter) super.initialize(getter, setter)
.also { this.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment } .also { this.coroutinesExperimentalCompatibilityMode = isExperimentalCoroutineInReleaseEnvironment }
} }
override fun createSubstitutedCopy( override fun createSubstitutedCopy(
@@ -176,7 +184,7 @@ class DeserializedClassConstructorDescriptor(
) : DeserializedCallableMemberDescriptor, ) : DeserializedCallableMemberDescriptor,
ClassConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, source ?: SourceElement.NO_SOURCE) { ClassConstructorDescriptorImpl(containingDeclaration, original, annotations, isPrimary, kind, source ?: SourceElement.NO_SOURCE) {
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
internal set internal set
override fun createSubstitutedCopy( override fun createSubstitutedCopy(
@@ -190,7 +198,7 @@ class DeserializedClassConstructorDescriptor(
return DeserializedClassConstructorDescriptor( return DeserializedClassConstructorDescriptor(
newOwner as ClassDescriptor, original as ConstructorDescriptor?, annotations, isPrimary, kind, newOwner as ClassDescriptor, original as ConstructorDescriptor?, annotations, isPrimary, kind,
proto, nameResolver, typeTable, versionRequirementTable, containerSource, source proto, nameResolver, typeTable, versionRequirementTable, containerSource, source
).also { it.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment } ).also { it.coroutinesExperimentalCompatibilityMode = coroutinesExperimentalCompatibilityMode }
} }
override fun isExternal(): Boolean = false override fun isExternal(): Boolean = false
@@ -222,14 +230,14 @@ class DeserializedTypeAliasDescriptor(
private lateinit var typeConstructorParameters: List<TypeParameterDescriptor> private lateinit var typeConstructorParameters: List<TypeParameterDescriptor>
private lateinit var defaultTypeImpl: SimpleType private lateinit var defaultTypeImpl: SimpleType
override var isExperimentalCoroutineInReleaseEnvironment: Boolean = false override var coroutinesExperimentalCompatibilityMode = DeserializedMemberDescriptor.CoroutinesCompatibilityMode.COMPATIBLE
private set private set
fun initialize( fun initialize(
declaredTypeParameters: List<TypeParameterDescriptor>, declaredTypeParameters: List<TypeParameterDescriptor>,
underlyingType: SimpleType, underlyingType: SimpleType,
expandedType: SimpleType, expandedType: SimpleType,
isExperimentalCoroutineInReleaseEnvironment: Boolean isExperimentalCoroutineInReleaseEnvironment: DeserializedMemberDescriptor.CoroutinesCompatibilityMode
) { ) {
initialize(declaredTypeParameters) initialize(declaredTypeParameters)
this.underlyingType = underlyingType this.underlyingType = underlyingType
@@ -237,7 +245,7 @@ class DeserializedTypeAliasDescriptor(
typeConstructorParameters = computeConstructorTypeParameters() typeConstructorParameters = computeConstructorTypeParameters()
defaultTypeImpl = computeDefaultType() defaultTypeImpl = computeDefaultType()
constructors = getTypeAliasConstructors() constructors = getTypeAliasConstructors()
this.isExperimentalCoroutineInReleaseEnvironment = isExperimentalCoroutineInReleaseEnvironment this.coroutinesExperimentalCompatibilityMode = isExperimentalCoroutineInReleaseEnvironment
} }
override val classDescriptor: ClassDescriptor? override val classDescriptor: ClassDescriptor?
@@ -255,7 +263,7 @@ class DeserializedTypeAliasDescriptor(
declaredTypeParameters, declaredTypeParameters,
substitutor.safeSubstitute(underlyingType, Variance.INVARIANT).asSimpleType(), substitutor.safeSubstitute(underlyingType, Variance.INVARIANT).asSimpleType(),
substitutor.safeSubstitute(expandedType, Variance.INVARIANT).asSimpleType(), substitutor.safeSubstitute(expandedType, Variance.INVARIANT).asSimpleType(),
isExperimentalCoroutineInReleaseEnvironment coroutinesExperimentalCompatibilityMode
) )
return substituted return substituted
@@ -3,7 +3,7 @@ import libO.*
suspend fun newMain() { suspend fun newMain() {
newFoo() newFoo()
<error descr="[DEPRECATION_ERROR] Using 'oldFoo(): Unit' is an error. Experimental coroutine cannot be used with API version 1.3">oldFoo</error>() <warning descr="[DEPRECATION] 'oldFoo(): Unit' is deprecated. Experimental coroutines support will be dropped in 1.4">oldFoo</warning>()
// TODO: actually, it's a bug // TODO: actually, it's a bug
oldMain() oldMain()
@@ -14,5 +14,5 @@ fun newMain2() {
newMain() newMain()
} }
<error descr="[DEPRECATION_ERROR] Using 'oldFoo(): Unit' is an error. Experimental coroutine cannot be used with API version 1.3"><error descr="[ILLEGAL_SUSPEND_FUNCTION_CALL] Suspend function 'oldFoo' should be called only from a coroutine or another suspend function">oldFoo</error></error>() <error descr="[ILLEGAL_SUSPEND_FUNCTION_CALL] Suspend function 'oldFoo' should be called only from a coroutine or another suspend function">oldFoo</error>()
} }