From b26b649d4eedfb32b1539fe0ecfa2401ebf3e399 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Thu, 11 May 2023 13:26:33 +0300 Subject: [PATCH] [FE] Introduce commonized version of expect/actual compatibility calculator This is needed to use the same code for those checks between both frontends and backend ^KT-58578 --- build.gradle.kts | 1 + ...bstractExpectActualCompatibilityChecker.kt | 500 ++++++++++++++++++ .../calls/mpp/ExpectActualMatchingContext.kt | 109 ++++ .../kotlin/mpp/DeclarationSymbolMarkers.kt | 26 + .../kotlin/types/model/MarkerExtensions.kt | 12 +- .../org/jetbrains/kotlin/utils/addToStdlib.kt | 6 +- 6 files changed, 646 insertions(+), 8 deletions(-) create mode 100644 compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt create mode 100644 compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt create mode 100644 core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt diff --git a/build.gradle.kts b/build.gradle.kts index 4e69addfd03..b6748a870a9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -355,6 +355,7 @@ val coreLibProjects by extra { val projectsWithEnabledContextReceivers by extra { listOf( ":core:descriptors.jvm", + ":compiler:resolution.common", ":compiler:frontend.common", ":compiler:fir:resolve", ":compiler:fir:plugin-utils", diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt new file mode 100644 index 00000000000..52da60b5644 --- /dev/null +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/AbstractExpectActualCompatibilityChecker.kt @@ -0,0 +1,500 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.mpp + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.mpp.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility +import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.addToStdlib.enumMapOf +import org.jetbrains.kotlin.utils.addToStdlib.enumSetOf +import org.jetbrains.kotlin.utils.keysToMap +import java.util.* + +object AbstractExpectActualCompatibilityChecker { + fun areCompatibleClassifiers( + expectClassSymbol: RegularClassSymbolMarker, + actualClassLikeSymbol: ClassLikeSymbolMarker, + context: ExpectActualMatchingContext, + ): ExpectActualCompatibility { + val result = with(context) { + areCompatibleClassifiers(expectClassSymbol, actualClassLikeSymbol) + } + @Suppress("UNCHECKED_CAST") + return result as ExpectActualCompatibility + } + + fun areCompatibleCallables( + expectDeclaration: CallableSymbolMarker, + actualDeclaration: CallableSymbolMarker, + parentSubstitutor: TypeSubstitutorMarker?, + expectContainingClass: RegularClassSymbolMarker?, + actualContainingClass: RegularClassSymbolMarker?, + context: ExpectActualMatchingContext, + ): ExpectActualCompatibility { + val result = with(context) { + areCompatibleCallables(expectDeclaration, actualDeclaration, parentSubstitutor, expectContainingClass, actualContainingClass) + } + @Suppress("UNCHECKED_CAST") + return result as ExpectActualCompatibility + } + + context(ExpectActualMatchingContext<*>) + @Suppress("warnings") + private fun areCompatibleClassifiers( + expectClassSymbol: RegularClassSymbolMarker, + actualClassLikeSymbol: ClassLikeSymbolMarker, + ): ExpectActualCompatibility<*> { + // Can't check FQ names here because nested expected class may be implemented via actual typealias's expansion with the other FQ name + require(expectClassSymbol.name == actualClassLikeSymbol.name) { + "This function should be invoked only for declarations with the same name: $expectClassSymbol, $actualClassLikeSymbol" + } + + val actualClass = when (actualClassLikeSymbol) { + is RegularClassSymbolMarker -> actualClassLikeSymbol + is TypeAliasSymbolMarker -> actualClassLikeSymbol.expandToRegularClass() + ?: return ExpectActualCompatibility.Compatible // do not report extra error on erroneous typealias + else -> error("Incorrect actual classifier for $expectClassSymbol: $actualClassLikeSymbol") + } + + if (expectClassSymbol.classKind != actualClass.classKind) return Incompatible.ClassKind + + if (!equalBy(expectClassSymbol, actualClass) { listOf(it.isCompanion, it.isInner, it.isInline || it.isValue) }) { + return Incompatible.ClassModifiers + } + + if (expectClassSymbol.isFun && !actualClass.isFun && actualClass.isNotSamInterface()) { + return Incompatible.FunInterfaceModifier + } + + val expectTypeParameterSymbols = expectClassSymbol.typeParameters + val actualTypeParameterSymbols = actualClass.typeParameters + if (expectTypeParameterSymbols.size != actualTypeParameterSymbols.size) { + return Incompatible.TypeParameterCount + } + + if (!areCompatibleModalities(expectClassSymbol.modality, actualClass.modality)) { + return Incompatible.Modality + } + + if (expectClassSymbol.visibility != actualClass.visibility) { + return Incompatible.Visibility + } + + val substitutor = createExpectActualTypeParameterSubstitutor( + expectTypeParameterSymbols, + actualTypeParameterSymbols, + parentSubstitutor = null + ) + + areCompatibleTypeParameters(expectTypeParameterSymbols, actualTypeParameterSymbols, substitutor).let { + if (it != ExpectActualCompatibility.Compatible) { + return it + } + } + + // Subtract kotlin.Any from supertypes because it's implicitly added if no explicit supertype is specified, + // and not added if an explicit supertype _is_ specified + val expectSupertypes = expectClassSymbol.superTypes.filterNot { it.typeConstructor().isAnyConstructor() } + val actualSupertypes = actualClass.superTypes.filterNot { it.typeConstructor().isAnyConstructor() } + if ( + expectSupertypes.map { substitutor.safeSubstitute(it) }.any { expectSupertype -> + actualSupertypes.none { actualSupertype -> + areCompatibleExpectActualTypes(expectSupertype, actualSupertype) + } + } + ) { + return Incompatible.Supertypes + } + + areCompatibleClassScopes(expectClassSymbol, actualClass, substitutor).let { + if (it != ExpectActualCompatibility.Compatible) { + return it + } + } + + return ExpectActualCompatibility.Compatible + } + + context(ExpectActualMatchingContext<*>) + private fun areCompatibleClassScopes( + expectClassSymbol: RegularClassSymbolMarker, + actualClassSymbol: RegularClassSymbolMarker, + substitutor: TypeSubstitutorMarker, + ): ExpectActualCompatibility<*> { + val unfulfilled = arrayListOf, MutableCollection>>>() + + val actualMembersByName = actualClassSymbol.collectAllMembers(isActualDeclaration = true).groupBy { it.name } + + outer@ for (expectMember in expectClassSymbol.collectAllMembers(isActualDeclaration = false)) { + if (expectMember is CallableSymbolMarker && expectMember.shouldSkipMatching(expectClassSymbol)) continue + + val actualMembers = actualMembersByName[expectMember.name]?.filter { actualMember -> + expectMember is CallableSymbolMarker && actualMember is CallableSymbolMarker || + expectMember is RegularClassSymbolMarker && actualMember is RegularClassSymbolMarker + }.orEmpty() + + val mapping = actualMembers.keysToMap { actualMember -> + when (expectMember) { + is CallableSymbolMarker -> areCompatibleCallables( + expectMember, + actualMember as CallableSymbolMarker, + substitutor, + expectClassSymbol, + actualClassSymbol + ) + + is RegularClassSymbolMarker -> areCompatibleClassifiers(expectMember, actualMember as RegularClassSymbolMarker) + else -> error("Unsupported declaration: $expectMember ($actualMembers)") + } + } + if (mapping.values.any { it == ExpectActualCompatibility.Compatible }) continue + + val incompatibilityMap = mutableMapOf, MutableCollection>() + for ((declaration, compatibility) in mapping) { + when (compatibility) { + ExpectActualCompatibility.Compatible -> continue@outer + is Incompatible -> incompatibilityMap.getOrPut(compatibility) { SmartList() }.add(declaration) + } + } + + unfulfilled.add(expectMember to incompatibilityMap) + } + + if (expectClassSymbol.classKind == ClassKind.ENUM_CLASS) { + val aEntries = expectClassSymbol.collectEnumEntryNames() + val bEntries = actualClassSymbol.collectEnumEntryNames() + + if (!bEntries.containsAll(aEntries)) return Incompatible.EnumEntries + } + + // TODO: check static scope? + + if (unfulfilled.isEmpty()) return ExpectActualCompatibility.Compatible + + return Incompatible.ClassScopes(unfulfilled) + } + + context(ExpectActualMatchingContext<*>) + private fun areCompatibleCallables( + expectDeclaration: CallableSymbolMarker, + actualDeclaration: CallableSymbolMarker, + parentSubstitutor: TypeSubstitutorMarker?, + expectContainingClass: RegularClassSymbolMarker?, + actualContainingClass: RegularClassSymbolMarker?, + ): ExpectActualCompatibility<*> { + require( + (expectDeclaration is ConstructorSymbolMarker && actualDeclaration is ConstructorSymbolMarker) || + expectDeclaration.callableId.callableName == actualDeclaration.callableId.callableName + ) { + "This function should be invoked only for declarations with the same name: $expectDeclaration, $actualDeclaration" + } + require((expectDeclaration.dispatchReceiverType == null) == (actualDeclaration.dispatchReceiverType == null)) { + "This function should be invoked only for declarations in the same kind of container (both members or both top level): $expectDeclaration, $actualDeclaration" + } + + if (expectDeclaration is FunctionSymbolMarker != actualDeclaration is FunctionSymbolMarker) { + return Incompatible.CallableKind + } + + val expectedReceiverType = expectDeclaration.extensionReceiverType + val actualReceiverType = actualDeclaration.extensionReceiverType + if ((expectedReceiverType != null) != (actualReceiverType != null)) { + return Incompatible.ParameterShape + } + + val expectedValueParameters = expectDeclaration.valueParameters + val actualValueParameters = actualDeclaration.valueParameters + if (!valueParametersCountCompatible(expectDeclaration, actualDeclaration, expectedValueParameters, actualValueParameters)) { + return Incompatible.ParameterCount + } + + val expectedTypeParameters = expectDeclaration.typeParameters + val actualTypeParameters = actualDeclaration.typeParameters + if (expectedTypeParameters.size != actualTypeParameters.size) { + return Incompatible.TypeParameterCount + } + + val substitutor = createExpectActualTypeParameterSubstitutor( + expectedTypeParameters, + actualTypeParameters, + parentSubstitutor + ) + + if ( + !areCompatibleTypeLists( + expectedValueParameters.toTypeList(substitutor), + actualValueParameters.toTypeList(createEmptySubstitutor()) + ) || + !areCompatibleExpectActualTypes( + expectedReceiverType?.let { substitutor.safeSubstitute(it) }, + actualReceiverType + ) + ) { + return Incompatible.ParameterTypes + } + + if (shouldCheckReturnTypesOfCallables) { + if (!areCompatibleExpectActualTypes(substitutor.safeSubstitute(expectDeclaration.returnType), actualDeclaration.returnType)) { + return Incompatible.ReturnType + } + } + + if (actualDeclaration.hasStableParameterNames && !equalsBy(expectedValueParameters, actualValueParameters) { it.name }) { + return Incompatible.ParameterNames + } + + if (!equalsBy(expectedTypeParameters, actualTypeParameters) { it.name }) { + return Incompatible.TypeParameterNames + } + + val expectModality = expectDeclaration.modality + val actualModality = actualDeclaration.modality + if ( + !areCompatibleModalities( + expectModality, + actualModality, + expectContainingClass?.modality, + actualContainingClass?.modality + ) + ) { + return Incompatible.Modality + } + + if (!areDeclarationsWithCompatibleVisibilities(expectDeclaration.visibility, expectModality, actualDeclaration.visibility)) { + return Incompatible.Visibility + } + + areCompatibleTypeParameters(expectedTypeParameters, actualTypeParameters, substitutor).let { + if (it != ExpectActualCompatibility.Compatible) { + return it + } + } + + if (!equalsBy(expectedValueParameters, actualValueParameters) { it.isVararg }) { + return Incompatible.ValueParameterVararg + } + + // Adding noinline/crossinline to parameters is disallowed, except if the expected declaration was not inline at all + if (expectDeclaration is SimpleFunctionSymbolMarker && expectDeclaration.isInline) { + if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isNoinline && actualValueParameters[i].isNoinline }) { + return Incompatible.ValueParameterNoinline + } + if (expectedValueParameters.indices.any { i -> !expectedValueParameters[i].isCrossinline && actualValueParameters[i].isCrossinline }) { + return Incompatible.ValueParameterCrossinline + } + } + + when { + expectDeclaration is FunctionSymbolMarker && actualDeclaration is FunctionSymbolMarker -> areCompatibleFunctions( + expectDeclaration, + actualDeclaration + ).let { if (it != ExpectActualCompatibility.Compatible) return it } + + expectDeclaration is PropertySymbolMarker && actualDeclaration is PropertySymbolMarker -> areCompatibleProperties( + expectDeclaration, + actualDeclaration + ).let { if (it != ExpectActualCompatibility.Compatible) return it } + + else -> error("Unsupported declarations: $expectDeclaration, $actualDeclaration") + } + + return ExpectActualCompatibility.Compatible + } + + context(ExpectActualMatchingContext<*>) + private fun valueParametersCountCompatible( + expectDeclaration: CallableSymbolMarker, + actualDeclaration: CallableSymbolMarker, + expectValueParameters: List, + actualValueParameters: List, + ): Boolean { + if (expectValueParameters.size == actualValueParameters.size) return true + + return if (expectDeclaration.isAnnotationConstructor() && actualDeclaration.isAnnotationConstructor()) { + expectValueParameters.isEmpty() && actualValueParameters.all { it.hasDefaultValue } + } else { + false + } + } + + context(ExpectActualMatchingContext<*>) + private fun areCompatibleTypeLists( + expectedTypes: List, + actualTypes: List, + ): Boolean { + for (i in expectedTypes.indices) { + if (!areCompatibleExpectActualTypes(expectedTypes[i], actualTypes[i])) { + return false + } + } + return true + } + + private fun areCompatibleModalities( + expectModality: Modality?, + actualModality: Modality?, + expectContainingClassModality: Modality? = null, + actualContainingClassModality: Modality? = null, + ): Boolean { + val expectEffectiveModality = effectiveModality(expectModality, expectContainingClassModality) + val actualEffectiveModality = effectiveModality(actualModality, actualContainingClassModality) + + return actualEffectiveModality in compatibleModalityMap.getValue(expectEffectiveModality) + } + + /* + * If containing class is final then all declarations in it effectively final + */ + private fun effectiveModality(declarationModality: Modality?, containingClassModality: Modality?): Modality? { + return when (containingClassModality) { + Modality.FINAL -> Modality.FINAL + else -> declarationModality + } + } + + /* + * Key is expect modality, value is a set of compatible actual modalities + */ + private val compatibleModalityMap: EnumMap> = enumMapOf( + Modality.ABSTRACT to enumSetOf(Modality.ABSTRACT), + Modality.OPEN to enumSetOf(Modality.OPEN), + Modality.FINAL to enumSetOf(Modality.OPEN, Modality.FINAL), + Modality.SEALED to enumSetOf(Modality.SEALED), + ) + + private fun areDeclarationsWithCompatibleVisibilities( + expectVisibility: Visibility, + expectModality: Modality?, + actualVisibility: Visibility, + ): Boolean { + val compare = Visibilities.compare(expectVisibility, actualVisibility) + return if (expectModality != Modality.FINAL) { + // For overridable declarations visibility should match precisely, see KT-19664 + compare == 0 + } else { + // For non-overridable declarations actuals are allowed to have more permissive visibility + compare != null && compare <= 0 + } + } + + context(ExpectActualMatchingContext<*>) + private fun areCompatibleTypeParameters( + expectTypeParameterSymbols: List, + actualTypeParameterSymbols: List, + substitutor: TypeSubstitutorMarker, + ): ExpectActualCompatibility<*> { + for (i in expectTypeParameterSymbols.indices) { + val expectBounds = expectTypeParameterSymbols[i].bounds + val actualBounds = actualTypeParameterSymbols[i].bounds + if ( + expectBounds.size != actualBounds.size || + !areCompatibleTypeLists(expectBounds.map { substitutor.safeSubstitute(it) }, actualBounds) + ) { + return Incompatible.TypeParameterUpperBounds + } + } + + if (!equalsBy(expectTypeParameterSymbols, actualTypeParameterSymbols) { it.variance }) { + return Incompatible.TypeParameterVariance + } + + // Removing "reified" from an expected function's type parameter is fine + if ( + expectTypeParameterSymbols.indices.any { i -> + !expectTypeParameterSymbols[i].isReified && actualTypeParameterSymbols[i].isReified + } + ) { + return Incompatible.TypeParameterReified + } + + return ExpectActualCompatibility.Compatible + } + + context(ExpectActualMatchingContext<*>) + private fun areCompatibleFunctions( + expectFunction: CallableSymbolMarker, + actualFunction: CallableSymbolMarker, + ): ExpectActualCompatibility<*> { + if (!equalBy(expectFunction, actualFunction) { f -> f.isSuspend }) { + return Incompatible.FunctionModifiersDifferent + } + + if ( + expectFunction.isExternal && !actualFunction.isExternal || + expectFunction.isInfix && !actualFunction.isInfix || + expectFunction.isInline && !actualFunction.isInline || + expectFunction.isOperator && !actualFunction.isOperator || + expectFunction.isTailrec && !actualFunction.isTailrec + ) { + return Incompatible.FunctionModifiersNotSubset + } + + return ExpectActualCompatibility.Compatible + } + + context(ExpectActualMatchingContext<*>) + private fun areCompatibleProperties( + expected: PropertySymbolMarker, + actual: PropertySymbolMarker, + ): ExpectActualCompatibility<*> { + return when { + !equalBy(expected, actual) { p -> p.isVar } -> Incompatible.PropertyKind + !equalBy(expected, actual) { p -> p.isLateinit } -> Incompatible.PropertyLateinitModifier + expected.isConst && !actual.isConst -> Incompatible.PropertyConstModifier + !arePropertySettersWithCompatibleVisibilities(expected, actual) -> Incompatible.PropertySetterVisibility + else -> ExpectActualCompatibility.Compatible + } + } + + context(ExpectActualMatchingContext<*>) + private fun arePropertySettersWithCompatibleVisibilities( + expected: PropertySymbolMarker, + actual: PropertySymbolMarker, + ): Boolean { + val expectedSetter = expected.setter ?: return true + val actualSetter = actual.setter ?: return true + return areDeclarationsWithCompatibleVisibilities(expectedSetter.visibility, expectedSetter.modality, actualSetter.visibility) + } + + // ---------------------------------------- Utils ---------------------------------------- + + context(ExpectActualMatchingContext<*>) + private fun List.toTypeList(substitutor: TypeSubstitutorMarker): List { + return this.map { substitutor.safeSubstitute(it.returnType) } + } + + private inline fun equalsBy(first: List, second: List, selector: (T) -> K): Boolean { + for (i in first.indices) { + if (selector(first[i]) != selector(second[i])) return false + } + + return true + } + + private inline fun equalBy(first: T, second: T, selector: (T) -> K): Boolean = + selector(first) == selector(second) + + context(ExpectActualMatchingContext<*>) + private val DeclarationSymbolMarker.name: Name + get() = when (this) { + is ConstructorSymbolMarker -> SpecialNames.INIT + is ValueParameterSymbolMarker -> parameterName + is CallableSymbolMarker -> callableId.callableName + is RegularClassSymbolMarker -> classId.shortClassName + is TypeAliasSymbolMarker -> classId.shortClassName + is TypeParameterSymbolMarker -> parameterName + else -> error("Unsupported declaration: $this") + } +} diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt new file mode 100644 index 00000000000..e53c9113e88 --- /dev/null +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/mpp/ExpectActualMatchingContext.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.calls.mpp + +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.mpp.* +import org.jetbrains.kotlin.name.CallableId +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.model.KotlinTypeMarker +import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker +import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext + +interface ExpectActualMatchingContext : TypeSystemInferenceExtensionContext { + val shouldCheckReturnTypesOfCallables: Boolean + + val RegularClassSymbolMarker.classId: ClassId + val TypeAliasSymbolMarker.classId: ClassId + val CallableSymbolMarker.callableId: CallableId + val TypeParameterSymbolMarker.parameterName: Name + val ValueParameterSymbolMarker.parameterName: Name + + fun TypeAliasSymbolMarker.expandToRegularClass(): RegularClassSymbolMarker? + + val RegularClassSymbolMarker.classKind: ClassKind + + val RegularClassSymbolMarker.isCompanion: Boolean + val RegularClassSymbolMarker.isInner: Boolean + val RegularClassSymbolMarker.isInline: Boolean + val RegularClassSymbolMarker.isValue: Boolean + val RegularClassSymbolMarker.isFun: Boolean + val ClassLikeSymbolMarker.typeParameters: List + + val ClassLikeSymbolMarker.modality: Modality? + val ClassLikeSymbolMarker.visibility: Visibility + + val CallableSymbolMarker.modality: Modality? + val CallableSymbolMarker.visibility: Visibility + + val RegularClassSymbolMarker.superTypes: List + + val CallableSymbolMarker.isExpect: Boolean + val CallableSymbolMarker.isInline: Boolean + val CallableSymbolMarker.isSuspend: Boolean + val CallableSymbolMarker.isExternal: Boolean + val CallableSymbolMarker.isInfix: Boolean + val CallableSymbolMarker.isOperator: Boolean + val CallableSymbolMarker.isTailrec: Boolean + + val PropertySymbolMarker.isVar: Boolean + val PropertySymbolMarker.isLateinit: Boolean + val PropertySymbolMarker.isConst: Boolean + + val PropertySymbolMarker.setter: FunctionSymbolMarker? + + fun createExpectActualTypeParameterSubstitutor( + expectTypeParameters: List, + actualTypeParameters: List, + parentSubstitutor: TypeSubstitutorMarker? + ): TypeSubstitutorMarker + + fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List + fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List + + fun RegularClassSymbolMarker.collectEnumEntryNames(): List + + val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker? + val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker? + val CallableSymbolMarker.returnType: KotlinTypeMarker + val CallableSymbolMarker.typeParameters: List + val FunctionSymbolMarker.valueParameters: List + + val CallableSymbolMarker.valueParameters: List + get() = (this as? FunctionSymbolMarker)?.valueParameters ?: emptyList() + + val ValueParameterSymbolMarker.isVararg: Boolean + val ValueParameterSymbolMarker.isNoinline: Boolean + val ValueParameterSymbolMarker.isCrossinline: Boolean + val ValueParameterSymbolMarker.hasDefaultValue: Boolean + + fun CallableSymbolMarker.isAnnotationConstructor(): Boolean + + val TypeParameterSymbolMarker.bounds: List + val TypeParameterSymbolMarker.variance: Variance + val TypeParameterSymbolMarker.isReified: Boolean + + fun areCompatibleExpectActualTypes( + expectType: KotlinTypeMarker?, + actualType: KotlinTypeMarker?, + ): Boolean + + fun RegularClassSymbolMarker.isNotSamInterface(): Boolean + + /* + * Determines should some declaration from expect class scope be checked + * - FE 1.0: skip fake overrides + * - FIR: skip nothing + * - IR: skip nothing + */ + fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean + + val CallableSymbolMarker.hasStableParameterNames: Boolean +} diff --git a/core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt b/core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt new file mode 100644 index 00000000000..1b45d03ef79 --- /dev/null +++ b/core/compiler.common/src/org/jetbrains/kotlin/mpp/DeclarationSymbolMarkers.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.mpp + +/* + * Those markers are needed for implementation of common algorithm of expect/actual + * compatibility checking, implemented in + * org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualCompatibilityChecker + */ +interface DeclarationSymbolMarker +interface CallableSymbolMarker : DeclarationSymbolMarker +interface FunctionSymbolMarker : CallableSymbolMarker +interface ConstructorSymbolMarker : FunctionSymbolMarker +interface SimpleFunctionSymbolMarker : FunctionSymbolMarker +interface PropertySymbolMarker : CallableSymbolMarker +interface ValueParameterSymbolMarker : CallableSymbolMarker + +interface ClassifierSymbolMarker : DeclarationSymbolMarker +interface TypeParameterSymbolMarker : ClassifierSymbolMarker +interface ClassLikeSymbolMarker : ClassifierSymbolMarker +interface RegularClassSymbolMarker : ClassLikeSymbolMarker +interface TypeAliasSymbolMarker : ClassLikeSymbolMarker + diff --git a/core/compiler.common/src/org/jetbrains/kotlin/types/model/MarkerExtensions.kt b/core/compiler.common/src/org/jetbrains/kotlin/types/model/MarkerExtensions.kt index 80077dbdd63..e73c3f8b887 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/types/model/MarkerExtensions.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/types/model/MarkerExtensions.kt @@ -9,22 +9,22 @@ fun TypeVariableMarker.freshTypeConstructor(c: TypeSystemInferenceExtensionConte fun TypeSubstitutorMarker.safeSubstitute( c: TypeSystemInferenceExtensionContext, type: KotlinTypeMarker -) = with(c) { safeSubstitute(type) } +): KotlinTypeMarker = with(c) { safeSubstitute(type) } -fun TypeVariableMarker.defaultType(c: TypeSystemInferenceExtensionContext) = with(c) { defaultType() } +fun TypeVariableMarker.defaultType(c: TypeSystemInferenceExtensionContext): SimpleTypeMarker = with(c) { defaultType() } -fun KotlinTypeMarker.dependsOnTypeConstructor(c: TypeSystemInferenceExtensionContext, typeConstructors: Set) = +fun KotlinTypeMarker.dependsOnTypeConstructor(c: TypeSystemInferenceExtensionContext, typeConstructors: Set): Boolean = with(c) { contains { it.typeConstructor() in typeConstructors } } -fun KotlinTypeMarker.dependsOnTypeParameters(c: TypeSystemInferenceExtensionContext, typeParameters: Collection) = +fun KotlinTypeMarker.dependsOnTypeParameters(c: TypeSystemInferenceExtensionContext, typeParameters: Collection): Boolean = with(c) { val typeConstructors = typeParameters.mapTo(mutableSetOf()) { it.getTypeConstructor() } dependsOnTypeConstructor(c, typeConstructors) } -fun CapturedTypeMarker.captureStatus(c: TypeSystemInferenceExtensionContext) = +fun CapturedTypeMarker.captureStatus(c: TypeSystemInferenceExtensionContext): CaptureStatus = with(c) { captureStatus() - } \ No newline at end of file + } diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt index 1958cd32b49..ee925e164aa 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt @@ -81,10 +81,12 @@ inline fun Iterable.partitionIsInstance(): Pair, List< return Pair(first, second) } -inline fun Iterable<*>.castAll(): Iterable { +@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") +@UnsafeCastFunction +inline fun List<*>.castAll(): List<@kotlin.internal.NoInfer T> { for (element in this) element as T @Suppress("UNCHECKED_CAST") - return this as Iterable + return this as List } fun sequenceOfLazyValues(vararg elements: () -> T): Sequence = elements.asSequence().map { it() }