[MPP] workaround inc/dec operator checks in expect classes
Implicit receiver type inside expect class is inconsistent with the type resolved from an explicit reference inside that class. The former one is a default class type, i.e. expect; the latter one resolves to an actual type when either expect and actual are in the same module, or when the sources are compiled for a particular platform. The workaround is to manually actualize implicit receiver type for one particular check affected by the broken subtyping. KT-49714
This commit is contained in:
committed by
teamcity
parent
90abbb4a9f
commit
0362d4ac9f
@@ -1,10 +0,0 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// SKIP_TXT
|
||||
// Issue: KT-49714
|
||||
|
||||
expect class Counter {
|
||||
operator fun inc(): Counter
|
||||
operator fun dec(): Counter
|
||||
}
|
||||
|
||||
actual typealias Counter = Int
|
||||
@@ -1,10 +1,11 @@
|
||||
// FIR_IDENTICAL
|
||||
// !LANGUAGE: +MultiPlatformProjects
|
||||
// SKIP_TXT
|
||||
// Issue: KT-49714
|
||||
|
||||
expect class Counter {
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun inc(): Counter
|
||||
<!INAPPLICABLE_OPERATOR_MODIFIER!>operator<!> fun dec(): Counter
|
||||
operator fun inc(): Counter
|
||||
operator fun dec(): Counter
|
||||
}
|
||||
|
||||
actual typealias Counter = Int
|
||||
|
||||
@@ -18,13 +18,13 @@ package org.jetbrains.kotlin.util
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.declaresOrInheritsDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
@@ -52,6 +52,7 @@ import org.jetbrains.kotlin.util.OperatorNameConventions.SIMPLE_UNARY_OPERATION_
|
||||
import org.jetbrains.kotlin.util.ReturnsCheck.*
|
||||
import org.jetbrains.kotlin.util.ValueParameterCountCheck.NoValueParameters
|
||||
import org.jetbrains.kotlin.util.ValueParameterCountCheck.SingleValueParameter
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
sealed class CheckResult(val isSuccess: Boolean) {
|
||||
class IllegalSignature(val error: String) : CheckResult(false)
|
||||
@@ -195,13 +196,43 @@ object OperatorChecks : AbstractModifierChecks() {
|
||||
Checks(SIMPLE_UNARY_OPERATION_NAMES, MemberOrExtension, NoValueParameters),
|
||||
Checks(listOf(INC, DEC), MemberOrExtension) {
|
||||
val receiver = dispatchReceiverParameter ?: extensionReceiverParameter
|
||||
ensure(receiver != null && (returnType?.isSubtypeOf(receiver.type) ?: false)) {
|
||||
ensure(receiver != null && ((returnType?.isSubtypeOf(receiver.type) ?: false) || incDecCheckForExpectClass(receiver))) {
|
||||
"receiver must be a supertype of the return type"
|
||||
}
|
||||
},
|
||||
Checks(ASSIGNMENT_OPERATIONS, MemberOrExtension, ReturnsUnit, SingleValueParameter, NoDefaultAndVarargsCheck),
|
||||
Checks(COMPONENT_REGEX, MemberOrExtension, NoValueParameters)
|
||||
) }
|
||||
)
|
||||
|
||||
/**
|
||||
* See KT-49714
|
||||
* Workaround for mismatching types of an implicit dispatch receiver inside an `expect` class
|
||||
* and a type resolved from a reference to this class. During compilation all actual type aliases are known,
|
||||
* so the explicit return type is `actual`. But the implicit receiver type inside the class remains `expect`
|
||||
* because it's received from the default type of the containing class, which is not affected by the `actual` type alias.
|
||||
*
|
||||
* `actual` classes are not affected, since non-parameterized type constructors with equal fqNames are considered
|
||||
* equal, so subtyping check passes in this case despite mismatching expect/actual in the corresponding declaration descriptors.
|
||||
*/
|
||||
private fun FunctionDescriptor.incDecCheckForExpectClass(receiver: ReceiverParameterDescriptor): Boolean {
|
||||
val receiverValue = receiver.value
|
||||
if (receiverValue !is ImplicitClassReceiver) return false
|
||||
|
||||
val classDescriptor = receiverValue.classDescriptor
|
||||
if (!classDescriptor.isExpect) return false
|
||||
|
||||
val potentialActualAliasId = classDescriptor.classId ?: return false
|
||||
val actualReceiverTypeAlias =
|
||||
classDescriptor.module.findClassifierAcrossModuleDependencies(potentialActualAliasId).safeAs<TypeAliasDescriptor>()
|
||||
?: return false
|
||||
|
||||
returnType?.let { returnType ->
|
||||
return returnType.isSubtypeOf(actualReceiverTypeAlias.expandedType)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
object InfixChecks : AbstractModifierChecks() {
|
||||
override val checks = listOf(
|
||||
|
||||
Reference in New Issue
Block a user