Clarify naming around inference fork points

^KT-43296 In Progress
This commit is contained in:
Denis.Zharkov
2022-11-03 13:21:17 +01:00
committed by Space Team
parent 715a73c8fb
commit 02b2927921
5 changed files with 53 additions and 29 deletions
@@ -62,8 +62,7 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c
) { ) {
val topLevelTypeVariables = topLevelType.extractTypeVariables() val topLevelTypeVariables = topLevelType.extractTypeVariables()
// NB: it's called in ConstraintSystemForks resolution stage by FE 1.0 resolveForkPointsConstraints()
processForkConstraints()
completion@ while (true) { completion@ while (true) {
// TODO: This is very slow // TODO: This is very slow
@@ -25,10 +25,29 @@ interface NewConstraintSystem {
fun asConstraintSystemCompleterContext(): ConstraintSystemCompletionContext fun asConstraintSystemCompleterContext(): ConstraintSystemCompletionContext
fun asPostponedArgumentsAnalyzerContext(): PostponedArgumentsAnalyzerContext fun asPostponedArgumentsAnalyzerContext(): PostponedArgumentsAnalyzerContext
fun processForkConstraints() fun resolveForkPointsConstraints()
fun getEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeInfo? fun getEmptyIntersectionTypeKind(types: Collection<KotlinTypeMarker>): EmptyIntersectionTypeInfo?
} }
typealias ForkPointData = List<ConstraintsFromSingleFork> /**
typealias ConstraintsFromSingleFork = Set<Pair<TypeVariableMarker, Constraint>> * In some cases we're not only adding constraints linearly to the system, but sometimes we need to consider several variants of constraints
*
* For example, from smartcast we've got a value of a type A<Int, String> & A<E, F> that we'd like to pass as an argument to the parameter
* of type A<Xv, Yv> (where Xv and Yv are the type variables of the current call)
*
* So, we've got a subtyping constraint
* A<Int, String> & A<E, F> <: A<Xv, Yv>
*
* And we might go with the first intersection component, having the following variables constraint set: {Xv=Int,Yv=String}
* Or, if we'd consider the second component it would be {Xv=E, Yv=F}
*
* And all existing and future constraints might work differently depending on which option we've chosen.
* Thus, ideally we need to create two versions of the constraint system and try to resolve each of them.
* But that lead to exponential complexity, so we only use some set of heuristics for that
*
* Lately, we call such situation a "fork point" and each of the options a "fork point branch"
* Each branch is defined by the set of constraints that need to be added to the system if we choose the particular branch.
*/
typealias ForkPointData = List<ForkPointBranchDescription>
typealias ForkPointBranchDescription = Set<Pair<TypeVariableMarker, Constraint>>
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintsFromSingleFork import org.jetbrains.kotlin.resolve.calls.inference.ForkPointBranchDescription
import org.jetbrains.kotlin.resolve.calls.inference.ForkPointData import org.jetbrains.kotlin.resolve.calls.inference.ForkPointData
import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind.* import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind.*
@@ -46,7 +46,7 @@ class ConstraintInjector(
constraints: MutableList<Pair<TypeVariableMarker, Constraint>> constraints: MutableList<Pair<TypeVariableMarker, Constraint>>
) )
fun processForkConstraints() fun resolveForkPointsConstraints()
} }
fun addInitialSubtypeConstraint(c: Context, lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition) { fun addInitialSubtypeConstraint(c: Context, lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition) {
@@ -141,7 +141,7 @@ class ConstraintInjector(
processConstraints(c, typeCheckerState, skipProperEqualityConstraints = false) processConstraints(c, typeCheckerState, skipProperEqualityConstraints = false)
} }
fun processForkConstraints( fun processGivenForkPointBranchConstraints(
c: Context, c: Context,
constraintSet: Collection<Pair<TypeVariableMarker, Constraint>>, constraintSet: Collection<Pair<TypeVariableMarker, Constraint>>,
position: IncorporationConstraintPosition position: IncorporationConstraintPosition
@@ -166,7 +166,7 @@ class ConstraintInjector(
// During completion, we start processing fork constrains immediately // During completion, we start processing fork constrains immediately
if (c.atCompletionState) { if (c.atCompletionState) {
c.processForkConstraints() c.resolveForkPointsConstraints()
} }
} }
} }
@@ -275,8 +275,8 @@ class ConstraintInjector(
private var possibleNewConstraints: MutableList<Pair<TypeVariableMarker, Constraint>>? = null private var possibleNewConstraints: MutableList<Pair<TypeVariableMarker, Constraint>>? = null
private var forkPointsData: MutableList<ForkPointData>? = null private var forkPointsData: MutableList<ForkPointData>? = null
private var stackForConstraintsSetsFromCurrentForkPoint: Stack<MutableList<ConstraintsFromSingleFork>>? = null private var stackForConstraintsSetsFromCurrentForkPoint: Stack<MutableList<ForkPointBranchDescription>>? = null
private var stackForCurrentConstraintSetFromSingleFork: Stack<MutableList<Pair<TypeVariableMarker, Constraint>>>? = null private var stackForConstraintSetFromCurrentForkPointBranch: Stack<MutableList<Pair<TypeVariableMarker, Constraint>>>? = null
override val isInferenceCompatibilityEnabled = languageVersionSettings.supportsFeature(LanguageFeature.InferenceCompatibility) override val isInferenceCompatibilityEnabled = languageVersionSettings.supportsFeature(LanguageFeature.InferenceCompatibility)
@@ -294,9 +294,9 @@ class ConstraintInjector(
fun addPossibleNewConstraint(variable: TypeVariableMarker, constraint: Constraint) { fun addPossibleNewConstraint(variable: TypeVariableMarker, constraint: Constraint) {
val constraintsSetsFromCurrentFork = stackForConstraintsSetsFromCurrentForkPoint?.lastOrNull() val constraintsSetsFromCurrentFork = stackForConstraintsSetsFromCurrentForkPoint?.lastOrNull()
if (constraintsSetsFromCurrentFork != null) { if (constraintsSetsFromCurrentFork != null) {
val currentConstraintSetForFork = stackForCurrentConstraintSetFromSingleFork?.lastOrNull() val currentConstraintSetForForkPointBranch = stackForConstraintSetFromCurrentForkPointBranch?.lastOrNull()
require(currentConstraintSetForFork != null) { "Constraint has been added not under fork {...} call " } require(currentConstraintSetForForkPointBranch != null) { "Constraint has been added not under fork {...} call " }
currentConstraintSetForFork.add(variable to constraint) currentConstraintSetForForkPointBranch.add(variable to constraint)
return return
} }
@@ -333,7 +333,7 @@ class ConstraintInjector(
) )
return true return true
} else if (constraintSets.size == 1) { } else if (constraintSets.size == 1) {
processForkConstraints( processGivenForkPointBranchConstraints(
c, c,
constraintSets.single(), constraintSets.single(),
position, position,
@@ -347,17 +347,17 @@ class ConstraintInjector(
var anyForkSuccessful = false var anyForkSuccessful = false
override fun fork(block: () -> Boolean) { override fun fork(block: () -> Boolean) {
if (stackForCurrentConstraintSetFromSingleFork == null) { if (stackForConstraintSetFromCurrentForkPointBranch == null) {
stackForCurrentConstraintSetFromSingleFork = SmartList() stackForConstraintSetFromCurrentForkPointBranch = SmartList()
} }
stackForCurrentConstraintSetFromSingleFork!!.add(SmartList()) stackForConstraintSetFromCurrentForkPointBranch!!.add(SmartList())
block().also { anyForkSuccessful = anyForkSuccessful || it } block().also { anyForkSuccessful = anyForkSuccessful || it }
stackForConstraintsSetsFromCurrentForkPoint!!.last() stackForConstraintsSetsFromCurrentForkPoint!!.last()
.addIfNotNull( .addIfNotNull(
stackForCurrentConstraintSetFromSingleFork?.popLast()?.takeIf { it.isNotEmpty() }?.toSet() stackForConstraintSetFromCurrentForkPointBranch?.popLast()?.takeIf { it.isNotEmpty() }?.toSet()
) )
} }
} }
@@ -11,7 +11,9 @@ import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.types.model.KotlinTypeMarker
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
import org.jetbrains.kotlin.types.model.TypeVariableMarker
abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context { abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Context, ResultTypeResolver.Context {
abstract val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker> abstract val allTypeVariables: Map<TypeConstructorMarker, TypeVariableMarker>
@@ -33,7 +35,7 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
abstract fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>) abstract fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>)
abstract fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean abstract fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean
abstract fun processForkConstraints() abstract fun resolveForkPointsConstraints()
fun <A : PostponedResolvedAtomMarker> analyzeArgumentWithFixedParameterTypes( fun <A : PostponedResolvedAtomMarker> analyzeArgumentWithFixedParameterTypes(
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
@@ -357,7 +357,11 @@ class NewConstraintSystemImpl(
return storage.constraintsFromAllForkPoints return storage.constraintsFromAllForkPoints
} }
override fun processForkConstraints() { /**
* This function tries to find the solution (set of constraints) that is consistent with some branch of each fork
* And those constraints are being immediately applied to the system
*/
override fun resolveForkPointsConstraints() {
if (constraintsFromAllForkPoints.isEmpty()) return if (constraintsFromAllForkPoints.isEmpty()) return
val allForkPointsData = constraintsFromAllForkPoints.toList() val allForkPointsData = constraintsFromAllForkPoints.toList()
constraintsFromAllForkPoints.clear() constraintsFromAllForkPoints.clear()
@@ -369,7 +373,7 @@ class NewConstraintSystemImpl(
// 1. {Xv=Int} is a one-element set (but potentially there might be more constraints in the set) // 1. {Xv=Int} is a one-element set (but potentially there might be more constraints in the set)
// 2. {Xv=T} second constraints set // 2. {Xv=T} second constraints set
for ((position, forkPointData) in allForkPointsData) { for ((position, forkPointData) in allForkPointsData) {
if (!processForkPointData(forkPointData, position)) { if (!applyConstraintsFromFirstSuccessfulBranchOfTheFork(forkPointData, position)) {
addError(NoSuccessfulFork(position)) addError(NoSuccessfulFork(position))
} }
} }
@@ -392,7 +396,7 @@ class NewConstraintSystemImpl(
var result: ConstraintSystemError? = null var result: ConstraintSystemError? = null
runTransaction { runTransaction {
for ((position, forkPointData) in allForkPointsData) { for ((position, forkPointData) in allForkPointsData) {
if (!processForkPointData(forkPointData, position)) { if (!applyConstraintsFromFirstSuccessfulBranchOfTheFork(forkPointData, position)) {
result = NoSuccessfulFork(position) result = NoSuccessfulFork(position)
break break
} }
@@ -409,20 +413,20 @@ class NewConstraintSystemImpl(
/** /**
* @return true if there is a successful constraints set for the fork * @return true if there is a successful constraints set for the fork
*/ */
private fun processForkPointData( private fun applyConstraintsFromFirstSuccessfulBranchOfTheFork(
forkPointData: ForkPointData, forkPointData: ForkPointData,
position: IncorporationConstraintPosition position: IncorporationConstraintPosition
): Boolean { ): Boolean {
return forkPointData.any { constraintSetForSingleFork -> return forkPointData.any { constraintSetForForkBranch ->
runTransaction { runTransaction {
constraintInjector.processForkConstraints( constraintInjector.processGivenForkPointBranchConstraints(
this@NewConstraintSystemImpl.apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }, this@NewConstraintSystemImpl.apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) },
constraintSetForSingleFork, constraintSetForForkBranch,
position, position,
) )
if (constraintsFromAllForkPoints.isNotEmpty()) { if (constraintsFromAllForkPoints.isNotEmpty()) {
processForkConstraints() resolveForkPointsConstraints()
} }
!hasContradiction !hasContradiction