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

org.jetbrains.kotlin.fir.resolve.calls.InferenceCompletion.kt Maven / Gradle / Ivy

/*
 * Copyright 2010-2019 JetBrains s.r.o. 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.resolve.calls

import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.resolve.calls.inference.components.KotlinConstraintSystemCompleter
import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator
import org.jetbrains.kotlin.resolve.calls.inference.components.VariableFixationFinder
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtom
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.isIntegerLiteralTypeConstructor
import org.jetbrains.kotlin.types.model.typeConstructor
import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs


fun Candidate.computeCompletionMode(
    components: InferenceComponents,
    expectedType: FirTypeRef?,
    currentReturnType: ConeKotlinType?
): KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode {
    // Presence of expected type means that we trying to complete outermost call => completion mode should be full
    if (expectedType != null) return KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL

    // This is questionable as null return type can be only for error call
    if (currentReturnType == null) return KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL

    return when {
        // Consider call foo(bar(x)), if return type of bar is a proper one, then we can complete resolve for bar => full completion mode
        // Otherwise, we shouldn't complete bar until we process call foo
        csBuilder.isProperType(currentReturnType) -> KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL

        // Nested call is connected with the outer one through the UPPER constraint (returnType <: expectedOuterType)
        // This means that there will be no new LOWER constraints =>
        //   it's possible to complete call now if there are proper LOWER constraints
        csBuilder.isTypeVariable(currentReturnType) ->
            if (hasProperNonTrivialLowerConstraints(components, currentReturnType))
                KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL
            else
                KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL

        // Return type has proper equal constraints => there is no need in the outer call
        containsTypeVariablesWithProperEqualConstraints(components, currentReturnType) -> KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL

        else -> KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.PARTIAL
    }
}

val Candidate.csBuilder get() = system.getBuilder()

private fun Candidate.containsTypeVariablesWithProperEqualConstraints(components: InferenceComponents, type: ConeKotlinType): Boolean =
    with(components.ctx){
        for ((variableConstructor, variableWithConstraints) in csBuilder.currentStorage().notFixedTypeVariables) {
            if (!type.contains { it.typeConstructor() == variableConstructor }) continue

            val constraints = variableWithConstraints.constraints
            val onlyProperEqualConstraints =
                constraints.isNotEmpty() && constraints.all { it.kind.isEqual() && csBuilder.isProperType(it.type) }

            if (!onlyProperEqualConstraints) return false
        }

        return true
    }

private fun Candidate.hasProperNonTrivialLowerConstraints(components: InferenceComponents, typeVariable: ConeKotlinType): Boolean {
    assert(csBuilder.isTypeVariable(typeVariable)) { "$typeVariable is not a type variable" }

    val context = components.ctx
    val constructor = typeVariable.typeConstructor(context)
    val variableWithConstraints = csBuilder.currentStorage().notFixedTypeVariables[constructor] ?: return false
    val constraints = variableWithConstraints.constraints
    return constraints.isNotEmpty() && constraints.all {
        !it.type.typeConstructor(context).isIntegerLiteralTypeConstructor(context) &&
                it.kind.isLower() && csBuilder.isProperType(it.type)
    }

}


class ConstraintSystemCompleter(val components: InferenceComponents) {
    val variableFixationFinder = VariableFixationFinder(components.trivialConstraintTypeInferenceOracle)
    fun complete(
        c: KotlinConstraintSystemCompleter.Context,
        completionMode: KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode,
        topLevelAtoms: List,
        candidateReturnType: ConeKotlinType,
        analyze: (PostponedResolvedAtomMarker) -> Unit
    ) {

        while (true) {
            if (analyzePostponeArgumentIfPossible(c, topLevelAtoms, analyze)) continue


//            val allTypeVariables = getOrderedAllTypeVariables(c, collectVariablesFromContext, topLevelAtoms)
            val allTypeVariables = c.notFixedTypeVariables.keys.toList()
            val postponedKtPrimitives = getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)
            val variableForFixation =
                variableFixationFinder.findFirstVariableForFixation(
                    c, allTypeVariables, postponedKtPrimitives, completionMode, candidateReturnType
                ) ?: break

//            if (shouldForceCallableReferenceOrLambdaResolution(completionMode, variableForFixation)) {
//                if (forcePostponedAtomResolution(topLevelAtoms, analyze)) continue
//                if (forcePostponedAtomResolution(topLevelAtoms, analyze)) continue
//            }

            if (variableForFixation.hasProperConstraint || completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL) {
                val variableWithConstraints = c.notFixedTypeVariables.getValue(variableForFixation.variable)

                fixVariable(c, candidateReturnType, variableWithConstraints, emptyList())

//                if (!variableForFixation.hasProperConstraint) {
//                    c.addError(NotEnoughInformationForTypeParameter(variableWithConstraints.typeVariable))
//                }
                continue
            }

            break
        }

        if (completionMode == KotlinConstraintSystemCompleter.ConstraintSystemCompletionMode.FULL) {
            // force resolution for all not-analyzed argument's
            getOrderedNotAnalyzedPostponedArguments(topLevelAtoms).forEach(analyze)
//
//            if (c.notFixedTypeVariables.isNotEmpty() && c.postponedTypeVariables.isEmpty()) {
//                runCompletion(c, completionMode, topLevelAtoms, topLevelType, analyze)
//            }
        }
    }

    private fun fixVariable(
        c: KotlinConstraintSystemCompleter.Context,
        topLevelType: KotlinTypeMarker,
        variableWithConstraints: VariableWithConstraints,
        postponedResolveKtPrimitives: List
    ) {
        val direction = TypeVariableDirectionCalculator(c, postponedResolveKtPrimitives, topLevelType).getDirection(variableWithConstraints)
        fixVariable(c, variableWithConstraints, direction)
    }

    fun fixVariable(
        c: KotlinConstraintSystemCompleter.Context,
        variableWithConstraints: VariableWithConstraints,
        direction: TypeVariableDirectionCalculator.ResolveDirection
    ) {
        val resultType = components.resultTypeResolver.findResultType(c, variableWithConstraints, direction)
        c.fixVariable(variableWithConstraints.typeVariable, resultType)
    }

    private fun analyzePostponeArgumentIfPossible(
        c: KotlinConstraintSystemCompleter.Context,
        topLevelAtoms: List,
        analyze: (PostponedResolvedAtomMarker) -> Unit
    ): Boolean {
        for (argument in getOrderedNotAnalyzedPostponedArguments(topLevelAtoms)) {
            if (canWeAnalyzeIt(c, argument)) {
                analyze(argument)
                return true
            }
        }
        return false
    }

    private fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List): List {
        fun FirStatement.process(to: MutableList) {
            when (this) {
                is FirFunctionCall -> {
                    val candidate = (this.calleeReference as? FirNamedReferenceWithCandidate)?.candidate
                    candidate?.postponedAtoms?.forEach {
                        to.addIfNotNull(it.safeAs()?.takeUnless { it.analyzed })
                    }
                    this.arguments.forEach { it.process(to) }
                }
                is FirWhenExpression -> {
                    val candidate = (this.calleeReference as? FirNamedReferenceWithCandidate)?.candidate
                    candidate?.postponedAtoms?.forEach {
                        to.addIfNotNull(it.safeAs()?.takeUnless { it.analyzed })
                    }
                    this.branches.forEach { it.result.process(to) }
                }

                is FirTryExpression -> {
                    val candidate = (this.calleeReference as? FirNamedReferenceWithCandidate)?.candidate
                    candidate?.postponedAtoms?.forEach {
                        to.addIfNotNull(it.safeAs()?.takeUnless { it.analyzed })
                    }
                    tryBlock.process(to)
                    catches.forEach { it.block.process(to) }
                }

                is FirWrappedArgumentExpression -> this.expression.process(to)
                // TOOD: WTF?
            }
//            if (analyzed) {
//                subResolvedAtoms.forEach { it.process(to) }
//            }
        }

        val notAnalyzedArguments = arrayListOf()
        for (primitive in topLevelAtoms) {
            primitive.process(notAnalyzedArguments)
        }

        return notAnalyzedArguments
    }

    private fun canWeAnalyzeIt(c: KotlinConstraintSystemCompleter.Context, argument: PostponedResolvedAtomMarker): Boolean {
        if (argument.analyzed) return false

        return argument.inputTypes.all { c.containsOnlyFixedOrPostponedVariables(it) }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy