From 0362d4ac9f4e4a653d11568305e6ddf937d2694c Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Thu, 27 Jan 2022 18:40:34 +0300 Subject: [PATCH] [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 --- .../tests/IncDecOperatorsInExpectClass.fir.kt | 10 ----- .../tests/IncDecOperatorsInExpectClass.kt | 5 ++- .../jetbrains/kotlin/util/modifierChecks.kt | 41 ++++++++++++++++--- 3 files changed, 39 insertions(+), 17 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.fir.kt diff --git a/compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.fir.kt b/compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.fir.kt deleted file mode 100644 index 1c6091c9cfb..00000000000 --- a/compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.fir.kt +++ /dev/null @@ -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 diff --git a/compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.kt b/compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.kt index 57314a99abb..cb90b5e2b99 100644 --- a/compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.kt +++ b/compiler/testData/diagnostics/tests/IncDecOperatorsInExpectClass.kt @@ -1,10 +1,11 @@ +// FIR_IDENTICAL // !LANGUAGE: +MultiPlatformProjects // SKIP_TXT // Issue: KT-49714 expect class Counter { - operator fun inc(): Counter - operator fun dec(): Counter + operator fun inc(): Counter + operator fun dec(): Counter } actual typealias Counter = Int diff --git a/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt b/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt index 17a71c8428a..1962698115e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/util/modifierChecks.kt @@ -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() + ?: return false + + returnType?.let { returnType -> + return returnType.isSubtypeOf(actualReceiverTypeAlias.expandedType) + } + + return false + } +} object InfixChecks : AbstractModifierChecks() { override val checks = listOf(