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

org.jetbrains.kotlin.fir.resolve.inference.FirDelegatedPropertyInferenceSession.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2020 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.resolve.inference

import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.FirResolvable
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.inference.model.ConeFixVariableConstraintPosition
import org.jetbrains.kotlin.fir.resolve.substitution.*
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.NewConstraintSystem
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionContext
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintSystemCompletionMode
import org.jetbrains.kotlin.resolve.calls.inference.model.BuilderInferencePosition
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.NewConstraintSystemImpl
import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariableIfNotPresent
import org.jetbrains.kotlin.types.model.*

class FirDelegatedPropertyInferenceSession(
    val property: FirProperty,
    resolutionContext: ResolutionContext,
    private val postponedArgumentsAnalyzer: PostponedArgumentsAnalyzer,
) : FirInferenceSessionForChainedResolve(resolutionContext) {

    private val currentConstraintSystem = components.session.inferenceComponents.createConstraintSystem()
    override val currentConstraintStorage: ConstraintStorage get() = currentConstraintSystem.currentStorage()

    private val unitType: ConeClassLikeType = components.session.builtinTypes.unitType.type
    private lateinit var resultingConstraintSystem: NewConstraintSystem

    private fun ConeKotlinType.containsStubType(): Boolean {
        return this.contains {
            it is ConeStubTypeForChainInference
        }
    }

    private fun integrateResolvedCall(storage: ConstraintStorage) {
        registerSyntheticVariables(storage)
        val stubToTypeVariableSubstitutor = createToSyntheticTypeVariableSubstitutor()
        integrateConstraints(
            currentConstraintSystem,
            storage,
            stubToTypeVariableSubstitutor,
            false
        )
    }

    override fun  addCompletedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {
        partiallyResolvedCalls += call to candidate
        if (candidate.isSuccessful) {
            integrateResolvedCall(candidate.system.asReadOnlyStorage())
        }
    }

    override fun  addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {
        super.addPartiallyResolvedCall(call)
        if (call.candidate.isSuccessful) {
            integrateResolvedCall(call.candidate.system.currentStorage())
        }
    }

    override fun  shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement {
        val callee = call.calleeReference as? FirNamedReferenceWithCandidate ?: return true

        if (callee.candidate.system.hasContradiction) return true

        val hasStubType =
            callee.candidate.chosenExtensionReceiver?.typeRef?.coneType?.containsStubType() ?: false
                    || callee.candidate.dispatchReceiver?.typeRef?.coneType?.containsStubType() ?: false

        if (!hasStubType) {
            return true
        }

        val system = call.candidate.system

        val storage = system.getBuilder().currentStorage()

        if (call.hasPostponed()) return true

        return storage.notFixedTypeVariables.keys.all {
            val variable = storage.allTypeVariables[it]
            val isPostponed = variable != null && variable in storage.postponedTypeVariables
            isPostponed || isSyntheticTypeVariable(variable!!) ||
                    components.callCompleter.completer.variableFixationFinder.isTypeVariableHasProperConstraint(system, it)
        }
    }

    private fun FirStatement.hasPostponed(): Boolean {
        var result = false
        processAllContainingCallCandidates(processBlocks = false) {
            result = result || it.hasPostponed()
        }
        return result
    }

    private fun Candidate.hasPostponed(): Boolean {
        return postponedAtoms.any { !it.analyzed }
    }

    override fun inferPostponedVariables(
        lambda: ResolvedLambdaAtom,
        constraintSystemBuilder: ConstraintSystemBuilder,
        completionMode: ConstraintSystemCompletionMode
    ): Map? = null

    private fun createNonFixedTypeToVariableSubstitutor(): NotFixedTypeToVariableSubstitutorForDelegateInference {
        val typeContext = components.session.typeContext

        val bindings = mutableMapOf()
        for ((variable, synthetic) in syntheticTypeVariableByTypeVariable) {
            bindings[synthetic] = variable.defaultType(typeContext) as ConeKotlinType
        }

        return NotFixedTypeToVariableSubstitutorForDelegateInference(bindings, typeContext)
    }


    /*
     * This creates Stub-preserving substitution to synthetic type variables
     * Stub(R) => Stub(_R)
     * R => _R
     */
    private fun createToSyntheticTypeVariableSubstitutor(): ConeSubstitutor {

        val typeContext = components.session.typeContext
        val bindings = mutableMapOf()
        for ((variable, syntheticVariable) in syntheticTypeVariableByTypeVariable) {
            bindings[variable.freshTypeConstructor(typeContext)] = syntheticVariable.defaultType
        }

        return typeContext.typeSubstitutorByTypeConstructor(bindings)
    }

    override fun hasSyntheticTypeVariables(): Boolean {
        return syntheticTypeVariableByTypeVariable.isNotEmpty()
    }

    override fun isSyntheticTypeVariable(typeVariable: TypeVariableMarker): Boolean {
        return typeVariable in syntheticTypeVariableByTypeVariable.values
    }

    override fun fixSyntheticTypeVariableWithNotEnoughInformation(
        typeVariable: TypeVariableMarker,
        completionContext: ConstraintSystemCompletionContext
    ) {
        typeVariable as ConeTypeVariable
        completionContext.fixVariable(
            typeVariable,
            ConeStubTypeForSyntheticFixation(
                ConeStubTypeConstructor(typeVariable, isTypeVariableInSubtyping = false, isForFixation = true),
                ConeNullability.create(typeVariable.defaultType.isMarkedNullable)
            ),
            ConeFixVariableConstraintPosition(typeVariable)
        )
    }

    fun completeCandidates(): List {
        val commonSystem = components.session.inferenceComponents.createConstraintSystem()

        val notCompletedCalls = partiallyResolvedCalls.mapNotNull { partiallyResolvedCall ->
            partiallyResolvedCall.first.takeIf { resolvable ->
                resolvable.candidate() != null
            }
        }

        val stubToTypeVariableSubstitutor = createNonFixedTypeToVariableSubstitutor()

        for ((call, candidate) in partiallyResolvedCalls) {
            if (candidate.isSuccessful) {
                integrateConstraints(
                    commonSystem,
                    candidate.system.asReadOnlyStorage(),
                    stubToTypeVariableSubstitutor,
                    call.candidate() != null
                )
            }
        }


        resolutionContext.bodyResolveContext.withInferenceSession(DEFAULT) {
            @Suppress("UNCHECKED_CAST")
            components.callCompleter.completer.complete(
                commonSystem.asConstraintSystemCompleterContext(),
                ConstraintSystemCompletionMode.FULL,
                notCompletedCalls as List,
                unitType, resolutionContext
            ) { lambdaAtom ->
                // Reversed here bc we want top-most call to avoid exponential visit
                val containingCandidateForLambda = notCompletedCalls.asReversed().first {
                    var found = false
                    it.processAllContainingCallCandidates(processBlocks = true) { subCandidate ->
                        if (subCandidate.postponedAtoms.contains(lambdaAtom)) {
                            found = true
                        }
                    }
                    found
                }.candidate
                postponedArgumentsAnalyzer.analyze(
                    commonSystem,
                    lambdaAtom,
                    containingCandidateForLambda,
                    ConstraintSystemCompletionMode.FULL,
                )
            }
        }

        for ((_, candidate) in partiallyResolvedCalls) {
            for (error in commonSystem.errors) {
                candidate.addDiagnostic(InferenceError(error))
            }
        }

        resultingConstraintSystem = commonSystem
        return notCompletedCalls
    }

    fun createFinalSubstitutor(): ConeSubstitutor {
        val stubTypeSubstitutor = createNonFixedTypeToVariableSubstitutor()

        val typeContext = components.session.typeContext
        val resultSubstitutor = resultingConstraintSystem.asReadOnlyStorage()
            .buildAbstractResultingSubstitutor(typeContext) as ConeSubstitutor
        return ChainedSubstitutor(stubTypeSubstitutor, resultSubstitutor)
            .replaceStubsAndTypeVariablesToErrors(typeContext, stubTypesByTypeVariable.values.map { it.constructor })
    }

    private val stubTypesByTypeVariable: MutableMap = mutableMapOf()
    private val syntheticTypeVariableByTypeVariable = mutableMapOf()

    private fun registerSyntheticVariables(storage: ConstraintStorage) {
        for (variableWithConstraints in storage.notFixedTypeVariables.values) {
            val variable = variableWithConstraints.typeVariable as ConeTypeVariable

            val syntheticVariable = syntheticTypeVariableByTypeVariable.getOrPut(variable) {
                ConeTypeVariable("+" + variable.typeConstructor.name).also {
                    currentConstraintSystem.registerVariable(it)
                }
            }

            stubTypesByTypeVariable.getOrPut(variable) {
                ConeStubTypeForChainInference(
                    syntheticVariable,
                    ConeNullability.create(syntheticVariable.defaultType.isMarkedNullable)
                )
            }
        }
    }

    override fun createSyntheticStubTypes(system: NewConstraintSystemImpl): Map {

        val bindings = mutableMapOf()
        registerSyntheticVariables(system.currentStorage())
        for (variable in system.postponedTypeVariables) {
            variable as ConeTypeVariable

            bindings[variable.typeConstructor] = stubTypesByTypeVariable[variable]!!
        }

        return bindings
    }

    override fun registerStubTypes(map: Map) {
//        @Suppress("UNCHECKED_CAST")
//        stubTypesByTypeVariable.putAll(map as Map)
    }


    private fun integrateConstraints(
        commonSystem: NewConstraintSystemImpl,
        storage: ConstraintStorage,
        nonFixedToVariablesSubstitutor: ConeSubstitutor,
        shouldIntegrateAllConstraints: Boolean
    ) {
        if (shouldIntegrateAllConstraints) {
            storage.notFixedTypeVariables.values.forEach {
                if (isSyntheticTypeVariable(it.typeVariable)) return@forEach
                commonSystem.registerTypeVariableIfNotPresent(it.typeVariable)
            }
        }
        /*
        * storage can contain the following substitutions:
        *  TypeVariable(A) -> ProperType
        *  TypeVariable(B) -> Special-Non-Fixed-Type
        *
        * while substitutor from parameter map non-fixed types to the original type variable
        * */
        val callSubstitutor =
            storage.buildAbstractResultingSubstitutor(commonSystem, transformTypeVariablesToErrorTypes = false) as ConeSubstitutor

        for (initialConstraint in storage.initialConstraints) {
            integrateConstraintToSystem(
                commonSystem, initialConstraint, callSubstitutor, nonFixedToVariablesSubstitutor, storage.fixedTypeVariables
            )
        }

        if (shouldIntegrateAllConstraints) {
            for ((variableConstructor, type) in storage.fixedTypeVariables) {
                val typeVariable = storage.allTypeVariables.getValue(variableConstructor)
                if (isSyntheticTypeVariable(typeVariable)) continue

                commonSystem.registerTypeVariableIfNotPresent(typeVariable)
                commonSystem.addEqualityConstraint((typeVariable as ConeTypeVariable).defaultType, type, BuilderInferencePosition)
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy