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

org.jetbrains.kotlin.fir.backend.IrBuiltInsOverFir.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2021 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.fir.backend

import org.jetbrains.kotlin.backend.common.ir.addDispatchReceiver
import org.jetbrains.kotlin.backend.common.serialization.signature.PublicIdSignatureComputer
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.descriptors.FirModuleDescriptor
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
import org.jetbrains.kotlin.fir.symbols.lazyDeclarationResolver
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.ir.BuiltInOperatorNames
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.*
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.util.OperatorNameConventions
import kotlin.reflect.KProperty

class IrBuiltInsOverFir(
    private val components: Fir2IrComponents,
    override val languageVersionSettings: LanguageVersionSettings,
    private val moduleDescriptor: FirModuleDescriptor,
    irMangler: KotlinMangler.IrMangler,
    private val tryLoadBuiltInsFirst: Boolean = false
) : IrBuiltIns() {

    override val irFactory: IrFactory = components.symbolTable.irFactory

    private val kotlinPackage = StandardClassIds.BASE_KOTLIN_PACKAGE
    private val kotlinInternalPackage = StandardClassIds.BASE_INTERNAL_PACKAGE

    override val operatorsPackageFragment = createPackage(KOTLIN_INTERNAL_IR_FQN)
    private val kotlinIrPackage = createPackage(kotlinPackage)
    private val kotlinInternalIrPackage = createPackage(kotlinInternalPackage)

    private val irSignatureBuilder = PublicIdSignatureComputer(irMangler)

    override val booleanNotSymbol: IrSimpleFunctionSymbol by lazy {
        boolean.ensureLazyContentsCreated()
        booleanClass.owner.functions.first { it.name == OperatorNameConventions.NOT && it.returnType == booleanType }.symbol
    }

    private val any by createClass(kotlinIrPackage, IdSignatureValues.any, build = { modality = Modality.OPEN }) {
        createConstructor()
        createMemberFunction("toString", stringType, modality = Modality.OPEN, isIntrinsicConst = false)
        createMemberFunction("hashCode", intType, modality = Modality.OPEN, isIntrinsicConst = false)
        createMemberFunction(
            OperatorNameConventions.EQUALS, booleanType, "other" to anyNType,
            modality = Modality.OPEN, isOperator = true, isIntrinsicConst = false
        )
    }
    override val anyClass: IrClassSymbol get() = any.klass
    override val anyType: IrType get() = any.type
    override val anyNType by lazy { anyType.makeNullable() }

    private val number by createClass(kotlinIrPackage, IdSignatureValues.number, build = { modality = Modality.ABSTRACT }) {
        configureSuperTypes()
        for (targetPrimitive in primitiveNumericIrTypes) {
            createMemberFunction("to${targetPrimitive.classFqName!!.shortName().asString()}", targetPrimitive, modality = Modality.ABSTRACT)
        }
        createMemberFunction("toChar", charType, modality = Modality.OPEN)
        finalizeClassDefinition()
    }
    override val numberClass: IrClassSymbol get() = number.klass
    override val numberType: IrType get() = number.type

    private val nothing by createClass(kotlinIrPackage, IdSignatureValues.nothing)
    override val nothingClass: IrClassSymbol get() = nothing.klass
    override val nothingType: IrType get() = nothing.type
    override val nothingNType: IrType by lazy { nothingType.makeNullable() }

    private val unit by createClass(kotlinIrPackage, IdSignatureValues.unit, build = { kind = ClassKind.OBJECT; modality = Modality.FINAL }) {
        configureSuperTypes()
        finalizeClassDefinition()
    }
    override val unitClass: IrClassSymbol get() = unit.klass
    override val unitType: IrType get() = unit.type

    private val boolean by createClass(kotlinIrPackage, IdSignatureValues._boolean) {
        configureSuperTypes()
        // TODO: dangerous dependency on call sequence, consider making extended BuiltInsClass to trigger lazy initialization
        createMemberFunction(OperatorNameConventions.NOT, booleanType, isOperator = true).symbol
        createMemberFunction(OperatorNameConventions.AND, booleanType, "other" to booleanType) { isInfix = true }
        createMemberFunction(OperatorNameConventions.OR, booleanType, "other" to booleanType) { isInfix = true }
        createMemberFunction(OperatorNameConventions.XOR, booleanType, "other" to booleanType) { isInfix = true }
        createMemberFunction(
            OperatorNameConventions.COMPARE_TO,
            intType,
            "other" to booleanType,
            modality = Modality.OPEN,
            isOperator = true
        )
        createIntrinsicConstOfToStringAndEquals()
        finalizeClassDefinition()
    }
    override val booleanType: IrType get() = boolean.type
    override val booleanClass: IrClassSymbol get() = boolean.klass

    private val char by createClass(kotlinIrPackage, IdSignatureValues._char) {
        configureSuperTypes(number)
        createStandardNumericAndCharMembers(charType)
        createMemberFunction(OperatorNameConventions.COMPARE_TO, intType, "other" to charType, modality = Modality.OPEN, isOperator = true)
        createMemberFunction(OperatorNameConventions.PLUS, charType, "other" to intType, isOperator = true)
        createMemberFunction(OperatorNameConventions.MINUS, charType, "other" to intType, isOperator = true)
        createMemberFunction(OperatorNameConventions.MINUS, intType, "other" to charType, isOperator = true)
        val charRange = referenceClassByClassId(StandardClassIds.CharRange)!!.owner.defaultType
        createMemberFunction(OperatorNameConventions.RANGE_TO, charRange, "other" to charType, isIntrinsicConst = false)
        createMemberFunction(OperatorNameConventions.RANGE_UNTIL, charRange, "other" to charType, isIntrinsicConst = false)
        createIntrinsicConstOfToStringAndEquals()
        finalizeClassDefinition()
    }
    override val charClass: IrClassSymbol get() = char.klass
    override val charType: IrType get() = char.type

    private val byte by kotlinIrPackage.createNumberClass(IdSignatureValues._byte)
    override val byteType: IrType get() = byte.type
    override val byteClass: IrClassSymbol get() = byte.klass

    private val short by kotlinIrPackage.createNumberClass(IdSignatureValues._short)
    override val shortType: IrType get() = short.type
    override val shortClass: IrClassSymbol get() = short.klass

    private val int by kotlinIrPackage.createNumberClass(IdSignatureValues._int)
    override val intType: IrType get() = int.type
    override val intClass: IrClassSymbol get() = int.klass

    private val long by kotlinIrPackage.createNumberClass(IdSignatureValues._long)
    override val longType: IrType get() = long.type
    override val longClass: IrClassSymbol get() = long.klass

    private val float by kotlinIrPackage.createNumberClass(IdSignatureValues._float)
    override val floatType: IrType get() = float.type
    override val floatClass: IrClassSymbol get() = float.klass

    private val double by kotlinIrPackage.createNumberClass(IdSignatureValues._double)
    override val doubleType: IrType get() = double.type
    override val doubleClass: IrClassSymbol get() = double.klass

    private val charSequence by createClass(
        kotlinIrPackage, IdSignatureValues.charSequence,
        build = { kind = ClassKind.INTERFACE; modality = Modality.OPEN }
    ) {
        configureSuperTypes()
        createProperty("length", intType, modality = Modality.ABSTRACT)
        createMemberFunction(OperatorNameConventions.GET, charType, "index" to intType, modality = Modality.ABSTRACT, isOperator = true, isIntrinsicConst = false)
        createMemberFunction("subSequence", defaultType, "startIndex" to intType, "endIndex" to intType, modality = Modality.ABSTRACT, isIntrinsicConst = false)
        finalizeClassDefinition()
    }
    override val charSequenceClass: IrClassSymbol get() = charSequence.klass

    private val string by createClass(kotlinIrPackage, IdSignatureValues.string) {
        configureSuperTypes(charSequence)
        createProperty("length", intType, modality = Modality.OPEN, isIntrinsicConst = true)
        createMemberFunction(OperatorNameConventions.GET, charType, "index" to intType, modality = Modality.OPEN, isOperator = true)
        createMemberFunction(
            "subSequence",
            charSequenceClass.defaultType,
            "startIndex" to intType,
            "endIndex" to intType,
            modality = Modality.OPEN,
            isIntrinsicConst = false
        )
        createMemberFunction(
            OperatorNameConventions.COMPARE_TO,
            intType,
            "other" to defaultType,
            modality = Modality.OPEN,
            isOperator = true
        )
        createMemberFunction(OperatorNameConventions.PLUS, defaultType, "other" to anyNType, isOperator = true)
        createIntrinsicConstOfToStringAndEquals()
        finalizeClassDefinition()
    }
    override val stringClass: IrClassSymbol get() = string.klass
    override val stringType: IrType get() = string.type

    private val intrinsicConstAnnotationFqName = kotlinInternalPackage.child(Name.identifier("IntrinsicConstEvaluation"))
    internal val intrinsicConst = kotlinInternalIrPackage.createClass(intrinsicConstAnnotationFqName).apply {
        owner.createConstructor()
        owner.finalizeClassDefinition()
    }

    private val intrinsicConstAnnotation: IrConstructorCall = run {
        val constructor = intrinsicConst.constructors.single()
        IrConstructorCallImpl.Companion.fromSymbolOwner(intrinsicConst.defaultType, constructor)
    }

    private val iterator by loadClass(StandardClassIds.Iterator)
    override val iteratorClass: IrClassSymbol get() = iterator.klass

    private val array by createClass(kotlinIrPackage, IdSignatureValues.array) {
        configureSuperTypes()
        val typeParameter = addTypeParameter("T", anyNType)
        addArrayMembers(typeParameter.defaultType, iteratorClass.typeWith(typeParameter.defaultType))
        finalizeClassDefinition()
    }
    override val arrayClass: IrClassSymbol get() = array.klass

    private val intRangeType by lazy { referenceClassByClassId(StandardClassIds.IntRange)!!.owner.defaultType }
    private val longRangeType by lazy { referenceClassByClassId(StandardClassIds.LongRange)!!.owner.defaultType }

    private val annotation by loadClass(StandardClassIds.Annotation)
    override val annotationClass: IrClassSymbol get() = annotation.klass
    override val annotationType: IrType get() = annotation.type

    private val collection by loadClass(StandardClassIds.Collection)
    override val collectionClass: IrClassSymbol get() = collection.klass
    private val set by loadClass(StandardClassIds.Set)
    override val setClass: IrClassSymbol get() = set.klass
    private val list by loadClass(StandardClassIds.List)
    override val listClass: IrClassSymbol get() = list.klass
    private val map by loadClass(StandardClassIds.Map)
    override val mapClass: IrClassSymbol get() = map.klass
    private val mapEntry by BuiltInsClass({ true to referenceClassByClassId(StandardClassIds.MapEntry)!! })
    override val mapEntryClass: IrClassSymbol get() = mapEntry.klass

    private val iterable by loadClass(StandardClassIds.Iterable)
    override val iterableClass: IrClassSymbol get() = iterable.klass
    private val listIterator by loadClass(StandardClassIds.ListIterator)
    override val listIteratorClass: IrClassSymbol get() = listIterator.klass
    private val mutableCollection by loadClass(StandardClassIds.MutableCollection)
    override val mutableCollectionClass: IrClassSymbol get() = mutableCollection.klass
    private val mutableSet by loadClass(StandardClassIds.MutableSet)
    override val mutableSetClass: IrClassSymbol get() = mutableSet.klass
    private val mutableList by loadClass(StandardClassIds.MutableList)
    override val mutableListClass: IrClassSymbol get() = mutableList.klass
    private val mutableMap by loadClass(StandardClassIds.MutableMap)
    override val mutableMapClass: IrClassSymbol get() = mutableMap.klass
    private val mutableMapEntry by BuiltInsClass({ true to referenceClassByClassId(StandardClassIds.MutableMapEntry)!! })
    override val mutableMapEntryClass: IrClassSymbol get() = mutableMapEntry.klass

    private val mutableIterable by loadClass(StandardClassIds.MutableIterable)
    override val mutableIterableClass: IrClassSymbol get() = mutableIterable.klass
    private val mutableIterator by loadClass(StandardClassIds.MutableIterator)
    override val mutableIteratorClass: IrClassSymbol get() = mutableIterator.klass
    private val mutableListIterator by loadClass(StandardClassIds.MutableListIterator)
    override val mutableListIteratorClass: IrClassSymbol get() = mutableListIterator.klass
    private val comparable by loadClass(StandardClassIds.Comparable)
    override val comparableClass: IrClassSymbol get() = comparable.klass
    override val throwableType: IrType by lazy { throwableClass.defaultType }
    private val throwable by loadClass(StandardClassIds.Throwable)
    override val throwableClass: IrClassSymbol get() = throwable.klass

    private val kCallable by loadClass(StandardClassIds.KCallable)
    override val kCallableClass: IrClassSymbol get() = kCallable.klass
    private val kProperty by loadClass(StandardClassIds.KProperty)
    override val kPropertyClass: IrClassSymbol get() = kProperty.klass
    private val kClass by loadClass(StandardClassIds.KClass)
    override val kClassClass: IrClassSymbol get() = kClass.klass
    private val kType by loadClass(StandardClassIds.KType)
    override val kTypeClass: IrClassSymbol get() = kType.klass
    private val kProperty0 by loadClass(StandardClassIds.KProperty0)
    override val kProperty0Class: IrClassSymbol get() = kProperty0.klass
    private val kProperty1 by loadClass(StandardClassIds.KProperty1)
    override val kProperty1Class: IrClassSymbol get() = kProperty1.klass
    private val kProperty2 by loadClass(StandardClassIds.KProperty2)
    override val kProperty2Class: IrClassSymbol get() = kProperty2.klass
    private val kMutableProperty0 by loadClass(StandardClassIds.KMutableProperty0)
    override val kMutableProperty0Class: IrClassSymbol get() = kMutableProperty0.klass
    private val kMutableProperty1 by loadClass(StandardClassIds.KMutableProperty1)
    override val kMutableProperty1Class: IrClassSymbol get() = kMutableProperty1.klass
    private val kMutableProperty2 by loadClass(StandardClassIds.KMutableProperty2)
    override val kMutableProperty2Class: IrClassSymbol get() = kMutableProperty2.klass

    private val function by loadClass(StandardClassIds.Function)
    override val functionClass: IrClassSymbol get() = function.klass
    private val kFunction by loadClass(StandardClassIds.KFunction)
    override val kFunctionClass: IrClassSymbol get() = kFunction.klass

    override val primitiveTypeToIrType = mapOf(
        PrimitiveType.BOOLEAN to booleanType,
        PrimitiveType.CHAR to charType,
        PrimitiveType.BYTE to byteType,
        PrimitiveType.SHORT to shortType,
        PrimitiveType.INT to intType,
        PrimitiveType.LONG to longType,
        PrimitiveType.FLOAT to floatType,
        PrimitiveType.DOUBLE to doubleType
    )

    private val primitiveIntegralIrTypes = listOf(byteType, shortType, intType, longType)
    override val primitiveFloatingPointIrTypes = listOf(floatType, doubleType)
    private val primitiveNumericIrTypes = primitiveIntegralIrTypes + primitiveFloatingPointIrTypes
    override val primitiveIrTypesWithComparisons = listOf(charType) + primitiveNumericIrTypes
    override val primitiveIrTypes = listOf(booleanType) + primitiveIrTypesWithComparisons
    private val baseIrTypes = primitiveIrTypes + stringType

    private val bitwiseOperators = arrayOf(OperatorNameConventions.AND, OperatorNameConventions.OR, OperatorNameConventions.XOR)
    private val shiftOperators = arrayOf(OperatorNameConventions.SHL, OperatorNameConventions.SHR, OperatorNameConventions.USHR)
    private val arithmeticOperators = arrayOf(
        OperatorNameConventions.PLUS,
        OperatorNameConventions.MINUS,
        OperatorNameConventions.TIMES,
        OperatorNameConventions.DIV,
        OperatorNameConventions.REM
    )

    private fun getPrimitiveArithmeticOperatorResultType(target: IrType, arg: IrType) =
        when {
            arg == doubleType -> arg
            target in primitiveFloatingPointIrTypes -> target
            arg in primitiveFloatingPointIrTypes -> arg
            target == longType -> target
            arg == longType -> arg
            else -> intType
        }

    private fun primitiveIterator(primitiveType: PrimitiveType) =
        loadClass(ClassId(StandardClassIds.BASE_COLLECTIONS_PACKAGE, Name.identifier("${primitiveType.typeName}Iterator")))

    private val booleanIterator by primitiveIterator(PrimitiveType.BOOLEAN)
    private val charIterator by primitiveIterator(PrimitiveType.CHAR)
    private val byteIterator by primitiveIterator(PrimitiveType.BYTE)
    private val shortIterator by primitiveIterator(PrimitiveType.SHORT)
    private val intIterator by primitiveIterator(PrimitiveType.INT)
    private val longIterator by primitiveIterator(PrimitiveType.LONG)
    private val floatIterator by primitiveIterator(PrimitiveType.FLOAT)
    private val doubleIterator by primitiveIterator(PrimitiveType.DOUBLE)

    private val _booleanArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.BOOLEAN, booleanIterator)
    private val _charArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.CHAR, charIterator)
    private val _byteArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.BYTE, byteIterator)
    private val _shortArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.SHORT, shortIterator)
    private val _intArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.INT, intIterator)
    private val _longArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.LONG, longIterator)
    private val _floatArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.FLOAT, floatIterator)
    private val _doubleArray by createPrimitiveArrayClass(kotlinIrPackage, PrimitiveType.DOUBLE, doubleIterator)

    override val booleanArray: IrClassSymbol get() = _booleanArray.klass
    override val charArray: IrClassSymbol get() = _charArray.klass
    override val byteArray: IrClassSymbol get() = _byteArray.klass
    override val shortArray: IrClassSymbol get() = _shortArray.klass
    override val intArray: IrClassSymbol get() = _intArray.klass
    override val longArray: IrClassSymbol get() = _longArray.klass
    override val floatArray: IrClassSymbol get() = _floatArray.klass
    override val doubleArray: IrClassSymbol get() = _doubleArray.klass

    override val primitiveArraysToPrimitiveTypes: Map by lazy {
        mapOf(
            booleanArray to PrimitiveType.BOOLEAN,
            charArray to PrimitiveType.CHAR,
            byteArray to PrimitiveType.BYTE,
            shortArray to PrimitiveType.SHORT,
            intArray to PrimitiveType.INT,
            longArray to PrimitiveType.LONG,
            floatArray to PrimitiveType.FLOAT,
            doubleArray to PrimitiveType.DOUBLE
        )
    }

    override val primitiveTypesToPrimitiveArrays get() = primitiveArraysToPrimitiveTypes.map { (k, v) -> v to k }.toMap()
    override val primitiveArrayElementTypes get() = primitiveArraysToPrimitiveTypes.mapValues { primitiveTypeToIrType[it.value] }
    override val primitiveArrayForType get() = primitiveArrayElementTypes.asSequence().associate { it.value to it.key }

    private val _ieee754equalsFunByOperandType = mutableMapOf()
    override val ieee754equalsFunByOperandType: MutableMap
        get() = _ieee754equalsFunByOperandType

    override var eqeqeqSymbol: IrSimpleFunctionSymbol private set
    override var eqeqSymbol: IrSimpleFunctionSymbol private set
    override var throwCceSymbol: IrSimpleFunctionSymbol private set
    override var throwIseSymbol: IrSimpleFunctionSymbol private set
    override var andandSymbol: IrSimpleFunctionSymbol private set
    override var ororSymbol: IrSimpleFunctionSymbol private set
    override var noWhenBranchMatchedExceptionSymbol: IrSimpleFunctionSymbol private set
    override var illegalArgumentExceptionSymbol: IrSimpleFunctionSymbol private set
    override var dataClassArrayMemberHashCodeSymbol: IrSimpleFunctionSymbol private set
    override var dataClassArrayMemberToStringSymbol: IrSimpleFunctionSymbol private set

    override var checkNotNullSymbol: IrSimpleFunctionSymbol private set
    override val arrayOfNulls: IrSimpleFunctionSymbol by lazy {
        findFunctions(kotlinPackage, Name.identifier("arrayOfNulls")).first {
            it.owner.dispatchReceiverParameter == null && it.owner.valueParameters.size == 1 &&
                    it.owner.valueParameters[0].type == intType
        }
    }

    override val linkageErrorSymbol: IrSimpleFunctionSymbol
        get() = TODO("Not yet implemented")

    override var lessFunByOperandType: Map private set
    override var lessOrEqualFunByOperandType: Map private set
    override var greaterOrEqualFunByOperandType: Map private set
    override var greaterFunByOperandType: Map private set

    init {
        with(this.operatorsPackageFragment) {

            fun addBuiltinOperatorSymbol(
                name: String,
                returnType: IrType,
                vararg valueParameterTypes: Pair,
                isIntrinsicConst: Boolean = false
            ) =
                createFunction(name, returnType, valueParameterTypes, origin = BUILTIN_OPERATOR, isIntrinsicConst = isIntrinsicConst).also {
                    declarations.add(it)
                }.symbol

            primitiveFloatingPointIrTypes.forEach { fpType ->
                _ieee754equalsFunByOperandType[fpType.classifierOrFail] = addBuiltinOperatorSymbol(
                    BuiltInOperatorNames.IEEE754_EQUALS,
                    booleanType,
                    "arg0" to fpType.makeNullable(),
                    "arg1" to fpType.makeNullable(),
                    isIntrinsicConst = true
                )
            }
            eqeqeqSymbol =
                addBuiltinOperatorSymbol(BuiltInOperatorNames.EQEQEQ, booleanType, "" to anyNType, "" to anyNType)
            eqeqSymbol =
                addBuiltinOperatorSymbol(BuiltInOperatorNames.EQEQ, booleanType, "" to anyNType, "" to anyNType, isIntrinsicConst = true)
            throwCceSymbol = addBuiltinOperatorSymbol(BuiltInOperatorNames.THROW_CCE, nothingType)
            throwIseSymbol = addBuiltinOperatorSymbol(BuiltInOperatorNames.THROW_ISE, nothingType)
            andandSymbol =
                addBuiltinOperatorSymbol(BuiltInOperatorNames.ANDAND, booleanType, "" to booleanType, "" to booleanType, isIntrinsicConst = true)
            ororSymbol =
                addBuiltinOperatorSymbol(BuiltInOperatorNames.OROR, booleanType, "" to booleanType, "" to booleanType, isIntrinsicConst = true)
            noWhenBranchMatchedExceptionSymbol =
                addBuiltinOperatorSymbol(BuiltInOperatorNames.NO_WHEN_BRANCH_MATCHED_EXCEPTION, nothingType)
            illegalArgumentExceptionSymbol =
                addBuiltinOperatorSymbol(BuiltInOperatorNames.ILLEGAL_ARGUMENT_EXCEPTION, nothingType, "" to stringType)
            dataClassArrayMemberHashCodeSymbol = addBuiltinOperatorSymbol("dataClassArrayMemberHashCode", intType, "" to anyType)
            dataClassArrayMemberToStringSymbol = addBuiltinOperatorSymbol("dataClassArrayMemberToString", stringType, "" to anyNType)

            checkNotNullSymbol = run {
                val typeParameter: IrTypeParameter = irFactory.createTypeParameter(
                    startOffset = UNDEFINED_OFFSET,
                    endOffset = UNDEFINED_OFFSET,
                    origin = BUILTIN_OPERATOR,
                    name = Name.identifier("T0"),
                    symbol = IrTypeParameterSymbolImpl(),
                    variance = Variance.INVARIANT,
                    index = 0,
                    isReified = true
                ).apply {
                    superTypes = listOf(anyType)
                }

                createFunction(
                    BuiltInOperatorNames.CHECK_NOT_NULL,
                    IrSimpleTypeImpl(typeParameter.symbol, SimpleTypeNullability.DEFINITELY_NOT_NULL, emptyList(), emptyList()),
                    arrayOf("" to IrSimpleTypeImpl(typeParameter.symbol, hasQuestionMark = true, emptyList(), emptyList())),
                    typeParameters = listOf(typeParameter),
                    origin = BUILTIN_OPERATOR
                ).also {
                    declarations.add(it)
                }.symbol
            }

            fun List.defineComparisonOperatorForEachIrType(name: String) =
                associate { it.classifierOrFail to addBuiltinOperatorSymbol(name, booleanType, "" to it, "" to it, isIntrinsicConst = true) }

            lessFunByOperandType = primitiveIrTypesWithComparisons.defineComparisonOperatorForEachIrType(BuiltInOperatorNames.LESS)
            lessOrEqualFunByOperandType =
                primitiveIrTypesWithComparisons.defineComparisonOperatorForEachIrType(BuiltInOperatorNames.LESS_OR_EQUAL)
            greaterOrEqualFunByOperandType =
                primitiveIrTypesWithComparisons.defineComparisonOperatorForEachIrType(BuiltInOperatorNames.GREATER_OR_EQUAL)
            greaterFunByOperandType = primitiveIrTypesWithComparisons.defineComparisonOperatorForEachIrType(BuiltInOperatorNames.GREATER)

        }
    }

    override val unsignedTypesToUnsignedArrays: Map by lazy {
        UnsignedType.values().mapNotNull { unsignedType ->
            val array = referenceClassByClassId(unsignedType.arrayClassId)
            if (array == null) null else unsignedType to array
        }.toMap()
    }

    override val unsignedArraysElementTypes: Map by lazy {
        unsignedTypesToUnsignedArrays.map { (k,v) -> v to referenceClassByClassId(k.classId)?.owner?.defaultType }.toMap()
    }

    override fun getKPropertyClass(mutable: Boolean, n: Int): IrClassSymbol = when (n) {
        0 -> if (mutable) kMutableProperty0Class else kProperty0Class
        1 -> if (mutable) kMutableProperty1Class else kProperty1Class
        2 -> if (mutable) kMutableProperty2Class else kProperty2Class
        else -> error("No KProperty for n=$n mutable=$mutable")
    }

    private val enum by loadClass(StandardClassIds.Enum)
    override val enumClass: IrClassSymbol get() = enum.klass

    override val intPlusSymbol: IrSimpleFunctionSymbol
        get() = intClass.functions.single {
            it.owner.name == OperatorNameConventions.PLUS && it.owner.valueParameters[0].type == intType
        }

    override val intTimesSymbol: IrSimpleFunctionSymbol
        get() = intClass.functions.single {
            it.owner.name == OperatorNameConventions.TIMES && it.owner.valueParameters[0].type == intType
        }

    override val intXorSymbol: IrSimpleFunctionSymbol
        get() = intClass.functions.single {
            it.owner.name == OperatorNameConventions.XOR && it.owner.valueParameters[0].type == intType
        }

    override val extensionToString: IrSimpleFunctionSymbol by lazy {
        findFunctions(kotlinPackage, OperatorNameConventions.TO_STRING).single { function ->
            function.owner.extensionReceiverParameter?.let { receiver -> receiver.type == anyNType } ?: false
        }
    }

    override val memberToString: IrSimpleFunctionSymbol by lazy {
        findBuiltInClassMemberFunctions(anyClass, OperatorNameConventions.TO_STRING).single { function ->
            function.owner.valueParameters.isEmpty()
        }
    }

    override val extensionStringPlus: IrSimpleFunctionSymbol by lazy {
        findFunctions(kotlinPackage, OperatorNameConventions.PLUS).single { function ->
            val isStringExtension =
                function.owner.extensionReceiverParameter?.let { receiver -> receiver.type == stringType.makeNullable() }
                    ?: false
            isStringExtension && function.owner.valueParameters.size == 1 && function.owner.valueParameters[0].type == anyNType
        }
    }

    override val memberStringPlus: IrSimpleFunctionSymbol by lazy {
        findBuiltInClassMemberFunctions(stringClass, OperatorNameConventions.PLUS).single { function ->
            function.owner.valueParameters.size == 1 && function.owner.valueParameters[0].type == anyNType
        }
    }

    override val arrayOf: IrSimpleFunctionSymbol by lazy {
        // distinct() is needed because we can get two Fir symbols for arrayOf function (from builtins and from stdlib)
        //   with the same IR symbol for them
        findFunctions(kotlinPackage, Name.identifier("arrayOf")).distinct().single()
    }

    private fun  getFunctionsByKey(
        name: Name,
        vararg packageNameSegments: String,
        makeKey: (IrSimpleFunctionSymbol) -> T?
    ): Map {
        val result = mutableMapOf()
        for (fn in findFunctions(name, *packageNameSegments)) {
            makeKey(fn)?.let { key ->
                result[key] = fn
            }
        }
        return result
    }

    override fun getNonBuiltInFunctionsByExtensionReceiver(
        name: Name, vararg packageNameSegments: String
    ): Map =
        getFunctionsByKey(name, *packageNameSegments) { fn ->
            fn.owner.extensionReceiverParameter?.type?.classifierOrNull
        }

    override fun getNonBuiltinFunctionsByReturnType(
        name: Name, vararg packageNameSegments: String
    ): Map =
        getFunctionsByKey(name, *packageNameSegments) { fn ->
            fn.owner.returnType.classOrNull
        }

    private val functionNMap = mutableMapOf()
    private val kFunctionNMap = mutableMapOf()
    private val suspendFunctionNMap = mutableMapOf()
    private val kSuspendFunctionNMap = mutableMapOf()

    override fun functionN(arity: Int): IrClass = functionNMap.getOrPut(arity) {
        referenceClassByClassId(StandardClassIds.FunctionN(arity))!!.owner
    }

    override fun kFunctionN(arity: Int): IrClass = kFunctionNMap.getOrPut(arity) {
        referenceClassByClassId(StandardClassIds.KFunctionN(arity))!!.owner
    }

    override fun suspendFunctionN(arity: Int): IrClass = suspendFunctionNMap.getOrPut(arity) {
        referenceClassByClassId(StandardClassIds.SuspendFunctionN(arity))!!.owner
    }

    override fun kSuspendFunctionN(arity: Int): IrClass = kSuspendFunctionNMap.getOrPut(arity) {
        referenceClassByClassId(StandardClassIds.KSuspendFunctionN(arity))!!.owner
    }

    override fun findFunctions(name: Name, vararg packageNameSegments: String): Iterable =
        findFunctions(FqName.fromSegments(packageNameSegments.asList()), name)

    override fun findFunctions(name: Name, packageFqName: FqName): Iterable =
        findFunctions(packageFqName, name)

    override fun findProperties(name: Name, packageFqName: FqName): Iterable =
        findProperties(packageFqName, name)

    override fun findClass(name: Name, vararg packageNameSegments: String): IrClassSymbol? =
        referenceClassByFqname(FqName.fromSegments(packageNameSegments.asList()), name)

    override fun findClass(name: Name, packageFqName: FqName): IrClassSymbol? =
        referenceClassByFqname(packageFqName, name)

    private fun referenceClassByFqname(packageName: FqName, identifier: Name) =
        referenceClassByClassId(ClassId(packageName, identifier))

    private val builtInClasses by lazy {
        setOf(anyClass)
    }

    override fun findBuiltInClassMemberFunctions(builtInClass: IrClassSymbol, name: Name): Iterable {
        require(builtInClass in builtInClasses)
        return builtInClass.functions.filter { it.owner.name == name }.asIterable()
    }

    override fun getBinaryOperator(name: Name, lhsType: IrType, rhsType: IrType): IrSimpleFunctionSymbol {
        val definingClass = lhsType.getMaybeBuiltinClass() ?: error("Defining class not found: $lhsType")
        return definingClass.functions.single { function ->
            function.name == name && function.valueParameters.size == 1 && function.valueParameters[0].type == rhsType
        }.symbol
    }

    override fun getUnaryOperator(name: Name, receiverType: IrType): IrSimpleFunctionSymbol {
        val definingClass = receiverType.getMaybeBuiltinClass() ?: error("Defining class not found: $receiverType")
        return definingClass.functions.single { function ->
            function.name == name && function.valueParameters.isEmpty()
        }.symbol
    }

// ---------------

    class BuiltInClassValue(
        private val generatedClass: IrClassSymbol,
        private var lazyContents: (IrClass.() -> Unit)?
    ) {
        fun ensureLazyContentsCreated() {
            if (lazyContents != null) synchronized(this) {
                lazyContents?.invoke(generatedClass.owner)
                lazyContents = null
            }
        }

        val klass: IrClassSymbol
            get() {
                ensureLazyContentsCreated()
                return generatedClass
            }

        val type: IrType get() = generatedClass.defaultType
    }

    private inner class BuiltInsClass(
        private var generator: (() -> Pair)?,
        private var lazyContents: (IrClass.() -> Unit)? = null
    ) {

        private var value: BuiltInClassValue? = null

        operator fun getValue(thisRef: Any?, property: KProperty<*>): BuiltInClassValue = value ?: run {
            synchronized(this) {
                if (value == null) {
                    val (isLoaded, symbol) = generator!!()
                    value = BuiltInClassValue(symbol, if (isLoaded) null else lazyContents)
                    generator = null
                    lazyContents = null
                }
            }
            value!!
        }
    }

    private fun loadClass(classId: ClassId) = BuiltInsClass({ true to referenceClassByClassId(classId)!! })

    private fun createClass(
        parent: IrDeclarationParent,
        signature: IdSignature.CommonSignature,
        build: IrClassBuilder.() -> Unit = {},
        lazyContents: (IrClass.() -> Unit) = { finalizeClassDefinition() }
    ) = BuiltInsClass(
        generator = {
            val loaded = if (tryLoadBuiltInsFirst) {
                referenceClassByClassId(ClassId(parent.kotlinFqName, Name.identifier(signature.shortName)))
            } else null
            (loaded != null) to (loaded ?: components.symbolTable.declareClass(
                signature,
                { IrClassPublicSymbolImpl(signature) },
                { symbol ->
                    IrClassBuilder().run {
                        name = Name.identifier(signature.shortName)
                        origin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
                        build()
                        irFactory.createClass(
                            startOffset = startOffset,
                            endOffset = endOffset,
                            origin = origin,
                            name = name,
                            visibility = visibility,
                            symbol = symbol,
                            kind = kind,
                            modality = modality,
                            isExternal = isExternal,
                            isCompanion = isCompanion,
                            isInner = isInner,
                            isData = isData,
                            isValue = isValue,
                            isExpect = isExpect,
                            isFun = isFun,
                        )
                    }.also {
                        it.parent = parent
                        it.createImplicitParameterDeclarationWithWrappedDescriptor()
                        components.symbolTable.declareClassWithSignature(irSignatureBuilder.computeSignature(it), it.symbol)
                    }
                }
            ).symbol)
        },
        lazyContents = lazyContents
    )

    private fun referenceClassByFqname(topLevelFqName: FqName) =
        referenceClassByClassId(ClassId.topLevel(topLevelFqName))

    private fun referenceClassByClassId(classId: ClassId): IrClassSymbol? {
        val firClassSymbol = components.session.symbolProvider.getClassLikeSymbolByClassId(classId) as? FirClassSymbol ?: return null
        firClassSymbol.lazyResolveToPhaseWithoutContractCheck(FirResolvePhase.STATUS)

        return components.classifierStorage.getIrClassSymbol(firClassSymbol)
    }

    private fun IrType.getMaybeBuiltinClass(): IrClass? {
        val lhsClassFqName = classFqName!!
        return baseIrTypes.find { it.classFqName == lhsClassFqName }?.getClass()
            ?: referenceClassByFqname(lhsClassFqName)?.owner
    }

    private fun createPackage(fqName: FqName): IrExternalPackageFragment =
        IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(moduleDescriptor, fqName)

    private fun IrDeclarationParent.createClass(
        fqName: FqName,
        vararg supertypes: IrType,
        classKind: ClassKind = ClassKind.CLASS,
        classModality: Modality = Modality.OPEN,
        builderBlock: IrClassBuilder.() -> Unit = {},
        block: IrClass.() -> Unit = {}
    ): IrClassSymbol {
        val signature = getPublicSignature(fqName.parent(), fqName.shortName().asString())

        return this.createClass(
            signature, *supertypes,
            classKind = classKind, classModality = classModality, builderBlock = builderBlock, block = block
        )
    }

    private fun IrDeclarationParent.createClass(
        signature: IdSignature.CommonSignature,
        vararg supertypes: IrType,
        classKind: ClassKind = ClassKind.CLASS,
        classModality: Modality = Modality.OPEN,
        builderBlock: IrClassBuilder.() -> Unit = {},
        block: IrClass.() -> Unit = {}
    ): IrClassSymbol = components.symbolTable.declareClass(
        signature,
        { IrClassPublicSymbolImpl(signature) },
        { symbol ->
            IrClassBuilder().run {
                name = Name.identifier(signature.shortName)
                kind = classKind
                modality = classModality
                origin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB
                builderBlock()
                irFactory.createClass(
                    startOffset = startOffset,
                    endOffset = endOffset,
                    origin = origin,
                    name = name,
                    visibility = visibility,
                    symbol = symbol,
                    kind = kind,
                    modality = modality,
                    isExternal = isExternal,
                    isCompanion = isCompanion,
                    isInner = isInner,
                    isData = isData,
                    isValue = isValue,
                    isExpect = isExpect,
                    isFun = isFun,
                )
            }.also {
                it.parent = this
                it.createImplicitParameterDeclarationWithWrappedDescriptor()
                it.block()
                it.superTypes = supertypes.asList()
            }
        }
    ).symbol

    private fun IrClass.createConstructor(
        origin: IrDeclarationOrigin = object : IrDeclarationOriginImpl("BUILTIN_CLASS_CONSTRUCTOR") {},
        isPrimary: Boolean = true,
        visibility: DescriptorVisibility = DescriptorVisibilities.PUBLIC,
        build: IrConstructor.() -> Unit = {}
    ): IrConstructorSymbol {
        val name = SpecialNames.INIT
        val ctor = irFactory.createConstructor(
            startOffset = UNDEFINED_OFFSET,
            endOffset = UNDEFINED_OFFSET,
            origin = origin,
            name = name,
            visibility = visibility,
            isInline = false,
            isExpect = false,
            returnType = defaultType,
            symbol = IrConstructorSymbolImpl(),
            isPrimary = isPrimary,
            isExternal = false,
        )
        ctor.parent = this
        ctor.build()
        declarations.add(ctor)
        components.symbolTable.declareConstructorWithSignature(
            irSignatureBuilder.computeSignature(ctor), ctor.symbol
        )
        return ctor.symbol
    }

    private fun IrClass.forEachSuperClass(body: IrClass.() -> Unit) {
        for (st in superTypes) {
            st.getClass()?.let {
                it.body()
                it.forEachSuperClass(body)
            }
        }
    }

    private fun IrClass.createMemberFunction(
        name: String, returnType: IrType, vararg valueParameterTypes: Pair,
        origin: IrDeclarationOrigin = object : IrDeclarationOriginImpl("BUILTIN_CLASS_METHOD") {},
        modality: Modality = Modality.FINAL,
        isOperator: Boolean = false,
        isInfix: Boolean = false,
        isIntrinsicConst: Boolean = true,
        build: IrFunctionBuilder.() -> Unit = {}
    ) = createFunction(
        name, returnType, valueParameterTypes,
        origin = origin, modality = modality, isOperator = isOperator, isInfix = isInfix, isIntrinsicConst = isIntrinsicConst,
        postBuild = {
            addDispatchReceiver { type = [email protected] }
        },
        build = build
    ).also { fn ->
        // very simple and fragile logic, but works for all current usages
        // TODO: replace with correct logic or explicit specification if cases become more complex
        forEachSuperClass {
            functions.find {
                it.name == fn.name && it.typeParameters.count() == fn.typeParameters.count() &&
                        it.valueParameters.count() == fn.valueParameters.count() &&
                        it.valueParameters.zip(fn.valueParameters).all { (l, r) -> l.type == r.type }
            }?.let {
                assert(it.symbol != fn) { "Cannot add function $fn to its own overriddenSymbols" }
                fn.overriddenSymbols += it.symbol
            }
        }

        declarations.add(fn)
    }

    private fun IrClass.createMemberFunction(
        name: Name, returnType: IrType, vararg valueParameterTypes: Pair,
        origin: IrDeclarationOrigin = object : IrDeclarationOriginImpl("BUILTIN_CLASS_METHOD") {},
        modality: Modality = Modality.FINAL,
        isOperator: Boolean = false,
        isInfix: Boolean = false,
        isIntrinsicConst: Boolean = true,
        build: IrFunctionBuilder.() -> Unit = {}
    ) =
        createMemberFunction(
            name.asString(), returnType, *valueParameterTypes,
            origin = origin, modality = modality, isOperator = isOperator, isInfix = isInfix,
            isIntrinsicConst = isIntrinsicConst, build = build
        )

    private fun IrClass.configureSuperTypes(vararg superTypes: BuiltInClassValue, defaultAny: Boolean = true) {
        for (superType in superTypes) {
            superType.ensureLazyContentsCreated()
        }
        if (!defaultAny || superTypes.contains(any) || this.superTypes.contains(anyType)) {
            this.superTypes += superTypes.map { it.type }
        } else {
            any.ensureLazyContentsCreated()
            this.superTypes += superTypes.map { it.type } + anyType
        }
    }

    private fun IrClass.finalizeClassDefinition() {
        addFakeOverrides(IrTypeSystemContextImpl(this@IrBuiltInsOverFir))
    }

    private fun IrDeclarationParent.createFunction(
        name: String,
        returnType: IrType,
        valueParameterTypes: Array>,
        typeParameters: List = emptyList(),
        origin: IrDeclarationOrigin = IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
        modality: Modality = Modality.FINAL,
        isOperator: Boolean = false,
        isInfix: Boolean = false,
        isIntrinsicConst: Boolean = false,
        postBuild: IrSimpleFunction.() -> Unit = {},
        build: IrFunctionBuilder.() -> Unit = {},
    ): IrSimpleFunction {

        fun makeWithSymbol(symbol: IrSimpleFunctionSymbol) = IrFunctionBuilder().run {
            this.name = Name.identifier(name)
            this.returnType = returnType
            this.origin = origin
            this.modality = modality
            this.isOperator = isOperator
            this.isInfix = isInfix
            build()
            irFactory.createSimpleFunction(
                startOffset = startOffset,
                endOffset = endOffset,
                origin = this.origin,
                name = this.name,
                visibility = visibility,
                isInline = isInline,
                isExpect = isExpect,
                returnType = this.returnType,
                modality = this.modality,
                symbol = symbol,
                isTailrec = isTailrec,
                isSuspend = isSuspend,
                isOperator = this.isOperator,
                isInfix = this.isInfix,
                isExternal = isExternal,
                containerSource = containerSource,
                isFakeOverride = isFakeOverride,
            )
        }.also { fn ->
            valueParameterTypes.forEachIndexed { index, (pName, irType) ->
                fn.addValueParameter(Name.identifier(pName.ifBlank { "arg$index" }), irType, origin)
            }
            fn.typeParameters = typeParameters
            typeParameters.forEach { it.parent = fn }
            if (isIntrinsicConst) {
                fn.annotations += intrinsicConstAnnotation
            }
            fn.parent = this@createFunction
            fn.postBuild()
        }

        val irFun4SignatureCalculation = makeWithSymbol(IrSimpleFunctionSymbolImpl())
        val signature = irSignatureBuilder.computeSignature(irFun4SignatureCalculation)
        return components.symbolTable.declareSimpleFunction(signature, { IrSimpleFunctionPublicSymbolImpl(signature, null) }, ::makeWithSymbol)
    }

    private fun IrClass.addArrayMembers(elementType: IrType, iteratorType: IrType) {
        addConstructor {
            origin = object : IrDeclarationOriginImpl("BUILTIN_CLASS_CONSTRUCTOR") {}
            returnType = defaultType
            isPrimary = true
        }.also {
            it.addValueParameter("size", intType, object : IrDeclarationOriginImpl("BUILTIN_CLASS_CONSTRUCTOR") {})
        }
        createMemberFunction(OperatorNameConventions.GET, elementType, "index" to intType, isOperator = true, isIntrinsicConst = false)
        createMemberFunction(OperatorNameConventions.SET, unitType, "index" to intType, "value" to elementType, isOperator = true, isIntrinsicConst = false)
        createProperty("size", intType)
        createMemberFunction(OperatorNameConventions.ITERATOR, iteratorType, isOperator = true)
    }

    private fun IrClass.createProperty(
        propertyName: String, returnType: IrType,
        modality: Modality = Modality.FINAL,
        isConst: Boolean = false, withGetter: Boolean = true, withField: Boolean = false, isIntrinsicConst: Boolean = false,
        fieldInit: IrExpression? = null,
        builder: IrProperty.() -> Unit = {}
    ) {
        addProperty {
            this.name = Name.identifier(propertyName)
            this.isConst = isConst
            this.modality = modality
        }.also { property ->

            // very simple and fragile logic, but works for all current usages
            // TODO: replace with correct logic or explicit specification if cases become more complex
            forEachSuperClass {
                properties.find { it.name == property.name }?.let {
                    assert(property != it.symbol) { "Cannot add property $property to its own overriddenSymbols"}
                    property.overriddenSymbols += it.symbol
                }
            }

            if (isIntrinsicConst) {
                property.annotations += intrinsicConstAnnotation
            }

            if (withGetter) {
                property.addGetter {
                    this.returnType = returnType
                    this.modality = modality
                    this.isOperator = false
                }.also { getter ->
                    getter.addDispatchReceiver { type = [email protected] }
                    getter.overriddenSymbols = property.overriddenSymbols.mapNotNull { it.owner.getter?.symbol }
                }
            }
            if (withField || fieldInit != null) {
                property.addBackingField {
                    this.type = returnType
                    this.isFinal = isConst
                }.also {
                    if (fieldInit != null) {
                        it.initializer = irFactory.createExpressionBody(
                            startOffset = 0,
                            endOffset = 0,
                            expression = fieldInit,
                        )
                    }
                }
            }
            property.builder()
            components.symbolTable.declarePropertyWithSignature(
                irSignatureBuilder.computeSignature(property), property.symbol
            )
            property.getter?.let {
                components.symbolTable.declareSimpleFunctionWithSignature(
                    irSignatureBuilder.computeSignature(it), it.symbol
                )
            }
            property.backingField?.let {
                components.symbolTable.declareFieldWithSignature(
                    irSignatureBuilder.computeSignature(it), it.symbol
                )
            }
        }
    }

    private class NumericConstantsExpressions(
        val min: IrConst,
        val max: IrConst,
        val sizeBytes: IrConst,
        val sizeBits: IrConst
    )

    private fun getNumericConstantsExpressions(type: IrType): NumericConstantsExpressions<*> {
        val so = UNDEFINED_OFFSET
        val eo = UNDEFINED_OFFSET
        return when (type.getPrimitiveType()) {
            PrimitiveType.CHAR -> NumericConstantsExpressions(
                IrConstImpl.char(so, eo, type, Char.MIN_VALUE), IrConstImpl.char(so, eo, type, Char.MAX_VALUE),
                IrConstImpl.int(so, eo, intType, Char.SIZE_BYTES), IrConstImpl.int(so, eo, intType, Char.SIZE_BITS)
            )
            PrimitiveType.BYTE -> NumericConstantsExpressions(
                IrConstImpl.byte(so, eo, type, Byte.MIN_VALUE), IrConstImpl.byte(so, eo, type, Byte.MAX_VALUE),
                IrConstImpl.int(so, eo, intType, Byte.SIZE_BYTES), IrConstImpl.int(so, eo, intType, Byte.SIZE_BITS)
            )
            PrimitiveType.SHORT -> NumericConstantsExpressions(
                IrConstImpl.short(so, eo, type, Short.MIN_VALUE), IrConstImpl.short(so, eo, type, Short.MAX_VALUE),
                IrConstImpl.int(so, eo, intType, Short.SIZE_BYTES), IrConstImpl.int(so, eo, intType, Short.SIZE_BITS)
            )
            PrimitiveType.INT -> NumericConstantsExpressions(
                IrConstImpl.int(so, eo, type, Int.MIN_VALUE), IrConstImpl.int(so, eo, type, Int.MAX_VALUE),
                IrConstImpl.int(so, eo, intType, Int.SIZE_BYTES), IrConstImpl.int(so, eo, intType, Int.SIZE_BITS)
            )
            PrimitiveType.LONG -> NumericConstantsExpressions(
                IrConstImpl.long(so, eo, type, Long.MIN_VALUE), IrConstImpl.long(so, eo, type, Long.MAX_VALUE),
                IrConstImpl.int(so, eo, intType, Long.SIZE_BYTES), IrConstImpl.int(so, eo, intType, Long.SIZE_BITS)
            )
            PrimitiveType.FLOAT -> NumericConstantsExpressions(
                IrConstImpl.float(so, eo, type, Float.MIN_VALUE), IrConstImpl.float(so, eo, type, Float.MAX_VALUE),
                IrConstImpl.int(so, eo, intType, Float.SIZE_BYTES), IrConstImpl.int(so, eo, intType, Float.SIZE_BITS)
            )
            PrimitiveType.DOUBLE -> NumericConstantsExpressions(
                IrConstImpl.double(so, eo, type, Double.MIN_VALUE), IrConstImpl.double(so, eo, type, Double.MAX_VALUE),
                IrConstImpl.int(so, eo, intType, Double.SIZE_BYTES), IrConstImpl.int(so, eo, intType, Double.SIZE_BITS)
            )
            else -> error("unsupported type")
        }
    }

    private fun IrPackageFragment.createNumberClass(
        signature: IdSignature.CommonSignature,
        lazyContents: (IrClass.() -> Unit)? = null
    ) =
        createClass(this, signature) {
            configureSuperTypes(number)
            val thisType = defaultType
            createStandardNumericAndCharMembers(thisType)
            createStandardNumericMembers(thisType)
            if (thisType in primitiveIntegralIrTypes) {
                createStandardRangeMembers(thisType)
            }
            if (thisType == intType || thisType == longType) {
                createStandardBitwiseOps(thisType)
            }
            lazyContents?.invoke(this)
            createIntrinsicConstOfToStringAndEquals()
            finalizeClassDefinition()
        }

    private fun createPrimitiveArrayClass(
        parent: IrDeclarationParent,
        primitiveType: PrimitiveType,
        primitiveIterator: BuiltInClassValue
    ) =
        createClass(
            parent,
            getPublicSignature(parent.kotlinFqName, primitiveType.arrayTypeName.asString()),
            build = { modality = Modality.FINAL }
        ) {
            configureSuperTypes()
            primitiveIterator.ensureLazyContentsCreated()
            addArrayMembers(primitiveTypeToIrType[primitiveType]!!, primitiveIterator.type)
            finalizeClassDefinition()
        }

    private fun IrClass.createCompanionObject(block: IrClass.() -> Unit = {}): IrClassSymbol =
        this.createClass(
            kotlinFqName.child(Name.identifier("Companion")), classKind = ClassKind.OBJECT, builderBlock = {
                isCompanion = true
            }
        ).also {
            it.owner.block()
            declarations.add(it.owner)
        }

    private fun IrClass.createStandardBitwiseOps(thisType: IrType) {
        for (op in bitwiseOperators) {
            createMemberFunction(op, thisType, "other" to thisType, isInfix = true)
        }
        for (op in shiftOperators) {
            createMemberFunction(op, thisType, "bitCount" to intType, isInfix = true)
        }
        createMemberFunction(OperatorNameConventions.INV, thisType)
    }

    private fun IrClass.createStandardRangeMembers(thisType: IrType) {
        for (argType in primitiveIntegralIrTypes) {
            createMemberFunction(
                OperatorNameConventions.RANGE_TO,
                if (thisType == longType || argType == longType) longRangeType else intRangeType,
                "other" to argType, isOperator = true, isIntrinsicConst = false
            )
        }
    }

    private fun IrClass.createStandardNumericMembers(thisType: IrType) {
        for (argument in primitiveNumericIrTypes) {
            createMemberFunction(
                OperatorNameConventions.COMPARE_TO, intType, "other" to argument,
                modality = if (argument == thisType) Modality.OPEN else Modality.FINAL,
                isOperator = true
            )
            val targetArithmeticReturnType = getPrimitiveArithmeticOperatorResultType(thisType, argument)
            for (op in arithmeticOperators) {
                createMemberFunction(op, targetArithmeticReturnType, "other" to argument, isOperator = true)
            }
        }
        val arithmeticReturnType = getPrimitiveArithmeticOperatorResultType(thisType, thisType)
        createMemberFunction(OperatorNameConventions.UNARY_PLUS, arithmeticReturnType, isOperator = true)
        createMemberFunction(OperatorNameConventions.UNARY_MINUS, arithmeticReturnType, isOperator = true)
    }

    private fun IrClass.createStandardNumericAndCharMembers(thisType: IrType) {
        createCompanionObject {
            val constExprs = getNumericConstantsExpressions(thisType)
            createProperty("MIN_VALUE", thisType, isConst = true, withGetter = false, fieldInit = constExprs.min)
            createProperty("MAX_VALUE", thisType, isConst = true, withGetter = false, fieldInit = constExprs.max)
            createProperty("SIZE_BYTES", intType, isConst = true, withGetter = false, fieldInit = constExprs.sizeBytes)
            createProperty("SIZE_BITS", intType, isConst = true, withGetter = false, fieldInit = constExprs.sizeBits)
        }
        for (targetPrimitive in primitiveIrTypesWithComparisons) {
            createMemberFunction("to${targetPrimitive.classFqName!!.shortName().asString()}", targetPrimitive, modality = Modality.OPEN)
        }
        createMemberFunction(OperatorNameConventions.INC, thisType, isOperator = true, isIntrinsicConst = false)
        createMemberFunction(OperatorNameConventions.DEC, thisType, isOperator = true, isIntrinsicConst = false)
    }

    private fun IrClass.createIntrinsicConstOfToStringAndEquals() {
        createMemberFunction(OperatorNameConventions.TO_STRING, stringType)
        createMemberFunction(
            OperatorNameConventions.EQUALS, booleanType, "other" to anyNType,
            modality = Modality.OPEN, isOperator = true
        )
    }

    private fun findFunctions(packageName: FqName, name: Name): List {
        return components.session.symbolProvider.getTopLevelFunctionSymbols(packageName, name)
            .onEach { it.lazyResolveToPhaseWithoutContractCheck(FirResolvePhase.STATUS) }
            .mapNotNull { components.declarationStorage.getIrFunctionSymbol(it) as? IrSimpleFunctionSymbol }
    }

    private fun findProperties(packageName: FqName, name: Name): List {
        return components.session.symbolProvider.getTopLevelPropertySymbols(packageName, name)
            .onEach { it.lazyResolveToPhaseWithoutContractCheck(FirResolvePhase.STATUS) }
            .mapNotNull { components.declarationStorage.getIrPropertySymbol(it) as? IrPropertySymbol }
    }

    private fun FirBasedSymbol<*>.lazyResolveToPhaseWithoutContractCheck(toPhase: FirResolvePhase) {
        val session = moduleData.session

        // In the compiler, the declaration should have been already resolved.
        // In the IDE, the contract check is not active.
        session.lazyDeclarationResolver.disableLazyResolveContractChecksInside {
            lazyResolveToPhase(toPhase)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy