All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.backend.common.actualizer.IrExpectActualMatchingContext.kt Maven / Gradle / Ivy

/*
 * 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.backend.common.actualizer

import org.jetbrains.kotlin.backend.common.sourceElement
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.descriptors.annotations.KotlinRetention
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.IrTypeBase
import org.jetbrains.kotlin.ir.types.impl.IrTypeProjectionImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.*
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.resolve.calls.mpp.ExpectActualCollectionArgumentsCompatibilityCheckStrategy
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext
import org.jetbrains.kotlin.resolve.calls.mpp.ExpectActualMatchingContext.AnnotationCallInfo
import org.jetbrains.kotlin.resolve.checkers.OptInNames
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.model.AnnotationMarker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
import org.jetbrains.kotlin.types.model.TypeSystemContext
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
import org.jetbrains.kotlin.utils.addToStdlib.castAll
import org.jetbrains.kotlin.utils.addToStdlib.shouldNotBeCalled

internal abstract class IrExpectActualMatchingContext(
    val typeContext: IrTypeSystemContext,
    val expectToActualClassMap: Map
) : ExpectActualMatchingContext, TypeSystemContext by typeContext {
    override val allowClassActualizationWithWiderVisibility: Boolean
        get() = true

    override val allowTransitiveSupertypesActualization: Boolean
        get() = true

    // This incompatibility is often suppressed in the source code (e.g. in kotlin-stdlib).
    // The backend must be able to do expect-actual matching to emit bytecode
    // That's why we disable the checker here. Probably, this checker can be enabled once KT-60426 is fixed
    override val shouldCheckAbsenceOfDefaultParamsInActual: Boolean
        get() = false

    private inline fun  CallableSymbolMarker.processIr(
        onFunction: (IrFunction) -> R,
        onProperty: (IrProperty) -> R,
        onField: (IrField) -> R,
        onValueParameter: (IrValueParameter) -> R,
        onEnumEntry: (IrEnumEntry) -> R,
    ): R {
        return when (this) {
            is IrFunctionSymbol -> onFunction(owner)
            is IrPropertySymbol -> onProperty(owner)
            is IrFieldSymbol -> onField(owner)
            is IrValueParameterSymbol -> onValueParameter(owner)
            is IrEnumEntrySymbol -> onEnumEntry(owner)
            else -> error("Unsupported declaration: $this")
        }
    }

    private inline fun  ClassLikeSymbolMarker.processIr(
        onClass: (IrClass) -> R,
        onTypeAlias: (IrTypeAlias) -> R,
    ): R {
        return when (this) {
            is IrClassSymbol -> onClass(owner)
            is IrTypeAliasSymbol -> onTypeAlias(owner)
            else -> error("Unsupported declaration: $this")
        }
    }

    private fun DeclarationSymbolMarker.asIr(): IrDeclaration = (this as IrSymbol).owner as IrDeclaration
    private fun FunctionSymbolMarker.asIr(): IrFunction = (this as IrFunctionSymbol).owner
    private fun PropertySymbolMarker.asIr(): IrProperty = (this as IrPropertySymbol).owner
    private fun ValueParameterSymbolMarker.asIr(): IrValueParameter = (this as IrValueParameterSymbol).owner
    private fun TypeParameterSymbolMarker.asIr(): IrTypeParameter = (this as IrTypeParameterSymbol).owner
    private fun RegularClassSymbolMarker.asIr(): IrClass = (this as IrClassSymbol).owner
    private fun TypeAliasSymbolMarker.asIr(): IrTypeAlias = (this as IrTypeAliasSymbol).owner

    private inline fun  DeclarationSymbolMarker.safeAsIr(): T? = (this as IrSymbol).owner as? T

    override val shouldCheckReturnTypesOfCallables: Boolean
        get() = true

    override val innerClassesCapturesOuterTypeParameters: Boolean
        get() = false

    override val enumConstructorsAreAlwaysCompatible: Boolean
        get() = true

    override val RegularClassSymbolMarker.classId: ClassId
        get() = asIr().classIdOrFail

    override val TypeAliasSymbolMarker.classId: ClassId
        get() = asIr().classIdOrFail

    override val CallableSymbolMarker.callableId: CallableId
        get() = processIr(
            onFunction = { it.callableId },
            onProperty = { it.callableId },
            onField = { it.callableId },
            onValueParameter = { shouldNotBeCalled() },
            onEnumEntry = { it.callableId }
        )

    override val TypeParameterSymbolMarker.parameterName: Name
        get() = asIr().name
    override val ValueParameterSymbolMarker.parameterName: Name
        get() = asIr().name

    override fun TypeAliasSymbolMarker.expandToRegularClass(): RegularClassSymbolMarker? {
        return asIr().expandedType.getClass()?.symbol
    }

    override val RegularClassSymbolMarker.classKind: ClassKind
        get() = asIr().kind
    override val RegularClassSymbolMarker.isCompanion: Boolean
        get() = asIr().isCompanion
    override val RegularClassSymbolMarker.isInner: Boolean
        get() = asIr().isInner
    override val RegularClassSymbolMarker.isInline: Boolean
        get() = asIr().isValue
    override val RegularClassSymbolMarker.isValue: Boolean
        get() = asIr().isValue

    override val RegularClassSymbolMarker.isFun: Boolean
        get() = asIr().isFun

    override val ClassLikeSymbolMarker.typeParameters: List
        get() {
            val parameters = processIr(
                onClass = { it.typeParameters },
                onTypeAlias = { it.typeParameters },
            )
            return parameters.map { it.symbol }
        }

    override val ClassLikeSymbolMarker.modality: Modality
        get() = processIr(
            onClass = {
                // For some reason kotlin annotations in IR have open modality and java annotations have final modality
                // But since it's legal to actualize kotlin annotation class with java annotation class
                //  and effectively all annotation classes have the same modality, it's ok to always return one
                //  modality for all annotation classes (doesn't matter final or open)
                if (it.isAnnotationClass) Modality.OPEN else it.modality
            },
            onTypeAlias = { Modality.FINAL }
        )
    override val ClassLikeSymbolMarker.visibility: Visibility
        get() = safeAsIr()!!.visibility.delegate

    override val CallableSymbolMarker.modality: Modality?
        get() = when (this) {
            is IrConstructorSymbol -> Modality.FINAL
            is IrSymbol -> (owner as? IrOverridableMember)?.modality
            else -> shouldNotBeCalled()
        }

    override val CallableSymbolMarker.visibility: Visibility
        get() = when (this) {
            is IrEnumEntrySymbol -> Visibilities.Public
            is IrSymbol -> (owner as IrDeclarationWithVisibility).visibility.delegate
            else -> shouldNotBeCalled()
        }

    override val RegularClassSymbolMarker.superTypes: List
        get() = asIr().superTypes

    override val RegularClassSymbolMarker.defaultType: KotlinTypeMarker
        get() = asIr().defaultType

    override val CallableSymbolMarker.isExpect: Boolean
        get() = processIr(
            onFunction = { it.isExpect },
            onProperty = { it.isExpect },
            onField = { false },
            onValueParameter = { false },
            onEnumEntry = { false }
        )
    override val CallableSymbolMarker.isInline: Boolean
        get() = processIr(
            onFunction = { it.isInline },
            onProperty = { false }, // property can not be inline in IR. Its getter is inline instead
            onField = { false },
            onValueParameter = { false },
            onEnumEntry = { false }
        )

    override val CallableSymbolMarker.isSuspend: Boolean
        get() = processIr(
            onFunction = { it.isSuspend },
            onProperty = { false }, // property can not be suspend in IR. Its getter is suspend instead
            onField = { false },
            onValueParameter = { false },
            onEnumEntry = { false }
        )

    override val CallableSymbolMarker.isExternal: Boolean
        get() = safeAsIr()?.isExternal ?: false

    override val CallableSymbolMarker.isInfix: Boolean
        get() = safeAsIr()?.isInfix ?: false

    override val CallableSymbolMarker.isOperator: Boolean
        get() = safeAsIr()?.isOperator ?: false

    override val CallableSymbolMarker.isTailrec: Boolean
        get() = safeAsIr()?.isTailrec ?: false

    override val PropertySymbolMarker.isVar: Boolean
        get() = asIr().isVar

    override val PropertySymbolMarker.isLateinit: Boolean
        get() = asIr().isLateinit

    override val PropertySymbolMarker.isConst: Boolean
        get() = asIr().isConst

    override val PropertySymbolMarker.setter: FunctionSymbolMarker?
        get() = asIr().setter?.symbol

    @OptIn(UnsafeCastFunction::class)
    override fun createExpectActualTypeParameterSubstitutor(
        expectTypeParameters: List,
        actualTypeParameters: List,
        parentSubstitutor: TypeSubstitutorMarker?,
    ): TypeSubstitutorMarker {
        val expectParameters = expectTypeParameters.castAll()
        val actualParameters = actualTypeParameters.castAll()
        val actualTypes = actualParameters.map { it.owner.defaultType }
        val substitutor = IrTypeSubstitutor(expectParameters, actualTypes, typeContext.irBuiltIns, allowEmptySubstitution = true)
        return when (parentSubstitutor) {
            null -> substitutor
            is AbstractIrTypeSubstitutor -> IrChainedSubstitutor(parentSubstitutor, substitutor)
            else -> shouldNotBeCalled()
        }
    }

    /*
     * [isActualDeclaration] flag is needed to correctly determine scope for specific class
     * In IR there are no scopes, all declarations are stored inside IrClass itself, so this flag
     *   has no sense in IR context
     */
    override fun RegularClassSymbolMarker.collectAllMembers(isActualDeclaration: Boolean): List {
        return asIr().declarations.filterNot { it is IrAnonymousInitializer }.map { it.symbol }
    }

    override fun RegularClassSymbolMarker.getMembersForExpectClass(name: Name): List {
        return asIr().declarations.filter { it.getNameWithAssert() == name }.map { it.symbol }
    }

    override fun RegularClassSymbolMarker.collectEnumEntryNames(): List {
        return asIr().declarations.filterIsInstance().map { it.name }
    }

    override val CallableSymbolMarker.dispatchReceiverType: KotlinTypeMarker?
        get() = (asIr().parent as? IrClass)?.defaultType

    override val CallableSymbolMarker.extensionReceiverType: KotlinTypeMarker?
        get() = safeAsIr()?.extensionReceiverParameter?.type

    override val CallableSymbolMarker.returnType: KotlinTypeMarker
        get() = processIr(
            onFunction = { it.returnType },
            onProperty = { it.getter?.returnType ?: it.backingField?.type ?: error("No type for property: $it") },
            onField = { it.type },
            onValueParameter = { it.type },
            onEnumEntry = { it.parentAsClass.defaultType }
        )

    override val CallableSymbolMarker.typeParameters: List
        get() = processIr(
            onFunction = { it.typeParameters.map { parameter -> parameter.symbol } },
            onProperty = { it.getter?.symbol?.typeParameters.orEmpty() },
            onField = { emptyList() },
            onValueParameter = { emptyList() },
            onEnumEntry = { emptyList() }
        )

    override fun FunctionSymbolMarker.allOverriddenDeclarationsRecursive(): Sequence =
        throw NotImplementedError("Not implemented because it's unused")

    override val FunctionSymbolMarker.valueParameters: List
        get() = asIr().valueParameters.map { it.symbol }

    override val ValueParameterSymbolMarker.isVararg: Boolean
        get() = asIr().isVararg
    override val ValueParameterSymbolMarker.isNoinline: Boolean
        get() = asIr().isNoinline
    override val ValueParameterSymbolMarker.isCrossinline: Boolean
        get() = asIr().isCrossinline
    override val ValueParameterSymbolMarker.hasDefaultValue: Boolean
        get() = asIr().hasDefaultValue()

    override fun CallableSymbolMarker.isAnnotationConstructor(): Boolean {
        val irConstructor = safeAsIr() ?: return false
        return irConstructor.constructedClass.isAnnotationClass
    }

    override val TypeParameterSymbolMarker.bounds: List
        get() = asIr().superTypes
    override val TypeParameterSymbolMarker.variance: Variance
        get() = asIr().variance
    override val TypeParameterSymbolMarker.isReified: Boolean
        get() = asIr().isReified

    override fun areCompatibleExpectActualTypes(expectType: KotlinTypeMarker?, actualType: KotlinTypeMarker?): Boolean {
        if (expectType == null) return actualType == null
        if (actualType == null) return false
        /*
         * Here we need to actualize both types, because of following situation:
         *
         *   // MODULE: common
         *   expect fun foo(): S // (1)
         *   expect class S
         *
         *   // MODULE: intermediate
         *   actual fun foo(): S = null!! // (2)
         *
         *   // MODULE: platform
         *   actual typealias S = String
         *
         * When we match return types of (1) and (2) they both will have original type `S`, but from
         *   perspective of module `platform` it should be replaced with `String`
         */
        val actualizedExpectType = expectType.actualize()
        val actualizedActualType = actualType.actualize()
        return AbstractTypeChecker.equalTypes(
            createTypeCheckerState(),
            actualizedExpectType,
            actualizedActualType
        )
    }

    private fun createTypeCheckerState(): TypeCheckerState {
        return typeContext.newTypeCheckerState(errorTypesEqualToAnything = true, stubTypesEqualToAnything = false)
    }

    override fun actualTypeIsSubtypeOfExpectType(expectType: KotlinTypeMarker, actualType: KotlinTypeMarker): Boolean {
        return AbstractTypeChecker.isSubtypeOf(
            createTypeCheckerState(),
            subType = actualType.actualize(),
            superType = expectType.actualize()
        )
    }

    private fun KotlinTypeMarker.actualize(): IrType {
        return actualizingSubstitutor.substitute(this as IrType)
    }

    private val actualizingSubstitutor = ActualizingSubstitutor()

    private inner class ActualizingSubstitutor : AbstractIrTypeSubstitutor() {
        override fun substitute(type: IrType): IrType {
            return substituteOrNull(type) ?: type
        }

        private fun substituteOrNull(type: IrType): IrType? {
            if (type !is IrSimpleTypeImpl) return null
            val newClassifier = (type.classifier.owner as? IrClass)?.let { expectToActualClassMap[it.classIdOrFail] }
            val newArguments = ArrayList(type.arguments.size)
            var argumentsChanged = false
            for (argument in type.arguments) {
                val newArgument = substituteArgumentOrNull(argument)
                if (newArgument != null) {
                    newArguments += newArgument
                    argumentsChanged = true
                } else {
                    newArguments += argument
                }
            }
            if (newClassifier == null && !argumentsChanged) {
                return null
            }
            return IrSimpleTypeImpl(
                classifier = newClassifier ?: type.classifier,
                type.nullability,
                newArguments,
                type.annotations,
                type.abbreviation
            )
        }

        private fun substituteArgumentOrNull(argument: IrTypeArgument): IrTypeArgument? {
            return when (argument) {
                is IrStarProjection -> null
                is IrTypeProjection -> when (argument) {
                    is IrTypeProjectionImpl -> {
                        val newType = substituteOrNull(argument.type) ?: return null
                        makeTypeProjection(newType, argument.variance)
                    }
                    is IrTypeBase -> substituteOrNull(argument) as IrTypeBase?
                    else -> shouldNotBeCalled()
                }
            }
        }
    }

    override fun RegularClassSymbolMarker.isNotSamInterface(): Boolean {
        /*
         * This is incorrect for java classes (because all java interfaces are considered as fun interfaces),
         *   but it's fine to not to check if some java interfaces is really SAM or not, because if one
         *   tries to actualize `expect fun interface` with typealias to non-SAM java interface, frontend
         *   will report an error and IR matching won't be invoked
         */
        return !asIr().isFun
    }

    override fun CallableSymbolMarker.shouldSkipMatching(containingExpectClass: RegularClassSymbolMarker): Boolean {
        return false
    }

    override val CallableSymbolMarker.hasStableParameterNames: Boolean
        get() {
            var ir = asIr()

            if (ir.isFakeOverride && ir is IrOverridableDeclaration<*>) {
                ir.resolveFakeOverrideOrNull()?.let { ir = it }
            }

            return when (ir.origin) {
                IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
                IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB,
                -> false
                else -> true
            }
        }

    override fun onMatchedMembers(expectSymbol: DeclarationSymbolMarker, actualSymbol: DeclarationSymbolMarker) {
        require(expectSymbol is IrSymbol)
        require(actualSymbol is IrSymbol)
        when (expectSymbol) {
            is IrClassSymbol -> {
                val actualClassSymbol = when (actualSymbol) {
                    is IrClassSymbol -> actualSymbol
                    is IrTypeAliasSymbol -> actualSymbol.owner.expandedType.getClass()!!.symbol
                    else -> actualSymbol.unexpectedSymbolKind()
                }
                onMatchedClasses(expectSymbol, actualClassSymbol)
            }
            else -> onMatchedCallables(expectSymbol, actualSymbol)
        }
    }

    abstract fun onMatchedClasses(expectClassSymbol: IrClassSymbol, actualClassSymbol: IrClassSymbol)
    abstract fun onMatchedCallables(expectSymbol: IrSymbol, actualSymbol: IrSymbol)

    override val DeclarationSymbolMarker.annotations: List
        get() = asIr().annotations.map(::AnnotationCallInfoImpl)

    override fun areAnnotationArgumentsEqual(
        expectAnnotation: AnnotationCallInfo, actualAnnotation: AnnotationCallInfo,
        collectionArgumentsCompatibilityCheckStrategy: ExpectActualCollectionArgumentsCompatibilityCheckStrategy,
    ): Boolean {
        fun AnnotationCallInfo.getIrElement(): IrConstructorCall = (this as AnnotationCallInfoImpl).irElement

        return areIrExpressionConstValuesEqual(
            expectAnnotation.getIrElement(),
            actualAnnotation.getIrElement(),
            collectionArgumentsCompatibilityCheckStrategy,
        )
    }

    internal fun getClassIdAfterActualization(classId: ClassId): ClassId {
        return expectToActualClassMap[classId]?.classId ?: classId
    }

    private inner class AnnotationCallInfoImpl(val irElement: IrConstructorCall) : AnnotationCallInfo {
        override val annotationSymbol: IrConstructorCall = irElement

        override val classId: ClassId?
            get() = getAnnotationClass()?.classId

        override val isRetentionSource: Boolean
            get() = getAnnotationClass()?.getAnnotationRetention() == KotlinRetention.SOURCE

        override val isOptIn: Boolean
            get() = getAnnotationClass()?.hasAnnotation(OptInNames.REQUIRES_OPT_IN_FQ_NAME) ?: false

        private fun getAnnotationClass(): IrClass? {
            val annotationClass = irElement.type.getClass() ?: return null
            return expectToActualClassMap[annotationClass.classId]?.owner ?: annotationClass
        }
    }

    override val DeclarationSymbolMarker.hasSourceAnnotationsErased: Boolean
        get() {
            val ir = asIr()
            return ir.sourceElement() == null && ir.origin !is IrDeclarationOrigin.GeneratedByPlugin
        }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy