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

org.jetbrains.kotlin.fir.types.TypeUnification.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
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.types

import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker

/**
 * @return false does only mean that there were conflicted values for some type parameter. In all other cases, it returns true.
 * "fail" result in the comments below means that we can't infer anything meaningful in that branch of unification.
 * See more at org.jetbrains.kotlin.types.TypeUnifier.doUnify.
 * NB: "Failed@ result of UnificationResultImpl is effectively unused in production.
 */
fun FirSession.doUnify(
    originalTypeProjection: ConeTypeProjection,
    typeWithParametersProjection: ConeTypeProjection,
    targetTypeParameters: Set,
    result: MutableMap,
): Boolean {
    val originalType = originalTypeProjection.type?.lowerBoundIfFlexible()
    val typeWithParameters = typeWithParametersProjection.type

    if (originalType is ConeIntersectionType) {
        val intersectionResult = mutableMapOf()
        for (intersectedType in originalType.intersectedTypes) {
            val localResult = mutableMapOf()
            if (!doUnify(intersectedType, typeWithParametersProjection, targetTypeParameters, localResult)) return false
            for ((typeParameter, typeProjection) in localResult) {
                val existingTypeProjection = intersectionResult[typeParameter]
                if (existingTypeProjection == null
                    || (typeProjection is KotlinTypeMarker &&
                            existingTypeProjection is KotlinTypeMarker &&
                            AbstractTypeChecker.isSubtypeOf(typeContext, typeProjection, existingTypeProjection))
                ) {
                    intersectionResult[typeParameter] = typeProjection
                }
            }
        }
        for ((key, value) in intersectionResult) {
            result[key] = value
        }
        return true
    }

    // in Foo ~ in X  =>  Foo ~ X
    if (originalTypeProjection.kind == typeWithParametersProjection.kind &&
        originalTypeProjection.kind != ProjectionKind.INVARIANT && originalTypeProjection.kind != ProjectionKind.STAR) {
        return doUnify(originalType!!, typeWithParameters!!, targetTypeParameters, result)
    }

    // Foo? ~ X?  =>  Foo ~ X
    if (originalType?.nullability == ConeNullability.NULLABLE && typeWithParameters?.nullability == ConeNullability.NULLABLE) {
        return doUnify(
            originalTypeProjection.removeQuestionMark(typeContext),
            typeWithParametersProjection.removeQuestionMark(typeContext),
            targetTypeParameters, result,
        )
    }

    // in Foo ~ out X  => fail
    // in Foo ~ X  =>  may be OK
    if (originalTypeProjection.kind != typeWithParametersProjection.kind && typeWithParametersProjection.kind != ProjectionKind.INVARIANT) {
        return true
    }

    if (typeWithParameters is ConeFlexibleType) {
        return doUnify(
            originalTypeProjection,
            typeWithParametersProjection.replaceType(typeWithParameters.lowerBound),
            targetTypeParameters, result,
        )
    }

    if (typeWithParameters is ConeDefinitelyNotNullType) {
        return doUnify(
            originalTypeProjection,
            typeWithParametersProjection.replaceType(typeWithParameters.original),
            targetTypeParameters, result,
        )
    }

    // Foo ~ X? => fail
    if (originalType?.nullability != ConeNullability.NULLABLE && typeWithParameters?.nullability == ConeNullability.NULLABLE) {
        return true
    }

    // Foo ~ X  =>  x |-> Foo
    // * ~ X => x |-> *
    val typeParameter = (typeWithParameters as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol
    if (typeParameter != null && typeParameter in targetTypeParameters) {
        if (typeParameter in result && result[typeParameter] != originalTypeProjection) return false
        result[typeParameter] = originalTypeProjection
        return true
    }

    // Foo? ~ Foo || in Foo ~ Foo || Foo ~ Bar
    if (originalType?.nullability?.isNullable != typeWithParameters?.nullability?.isNullable) return true
    if (originalTypeProjection.kind != typeWithParametersProjection.kind) return true
    if ((originalType as? ConeLookupTagBasedType)?.lookupTag != (typeWithParameters as? ConeLookupTagBasedType)?.lookupTag) return true
    if (originalType == null || typeWithParameters == null) return true

    // Foo ~ Foo
    if (originalType.typeArguments.size != typeWithParameters.typeArguments.size) {
        return true
    }

    // Foo ~ Foo
    if (originalType.typeArguments.isEmpty()) {
        return true
    }

    // Foo<...> ~ Foo<...>
    for ((originalTypeArgument, typeWithParametersArgument) in originalType.typeArguments.zip(typeWithParameters.typeArguments)) {
        if (!doUnify(originalTypeArgument, typeWithParametersArgument, targetTypeParameters, result)) return false
    }

    return true
}

private fun ConeTypeProjection.removeQuestionMark(typeContext: ConeTypeContext): ConeTypeProjection {
    val type = type
    require(type != null && type.nullability.isNullable) {
        "Expected nullable type, got $type"
    }

    return replaceType(type.withNullability(ConeNullability.NOT_NULL, typeContext))
}

private fun ConeTypeProjection.replaceType(newType: ConeKotlinType): ConeTypeProjection =
    when (kind) {
        ProjectionKind.INVARIANT -> newType
        ProjectionKind.IN -> ConeKotlinTypeProjectionIn(newType)
        ProjectionKind.OUT -> ConeKotlinTypeProjectionOut(newType)
        ProjectionKind.STAR -> error("Should not be a star projection")
    }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy