diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt index 1a4957c0a67..681241c2074 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/ConstraintSystemCompleter.kt @@ -62,8 +62,7 @@ class ConstraintSystemCompleter(components: BodyResolveComponents, private val c ) { val topLevelTypeVariables = topLevelType.extractTypeVariables() - // NB: it's called in ConstraintSystemForks resolution stage by FE 1.0 - processForkConstraints() + resolveForkPointsConstraints() completion@ while (true) { // TODO: This is very slow diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt index 21b8a5a17af..8541c12b0da 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/NewConstraintSystem.kt @@ -25,10 +25,29 @@ interface NewConstraintSystem { fun asConstraintSystemCompleterContext(): ConstraintSystemCompletionContext fun asPostponedArgumentsAnalyzerContext(): PostponedArgumentsAnalyzerContext - fun processForkConstraints() + fun resolveForkPointsConstraints() fun getEmptyIntersectionTypeKind(types: Collection): EmptyIntersectionTypeInfo? } -typealias ForkPointData = List -typealias ConstraintsFromSingleFork = Set> +/** + * 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 & A that we'd like to pass as an argument to the parameter + * of type A (where Xv and Yv are the type variables of the current call) + * + * So, we've got a subtyping constraint + * A & A <: A + * + * 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 +typealias ForkPointBranchDescription = Set> diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt index 5885bf45095..058fbd196f8 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings 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.model.* import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind.* @@ -46,7 +46,7 @@ class ConstraintInjector( constraints: MutableList> ) - fun processForkConstraints() + fun resolveForkPointsConstraints() } fun addInitialSubtypeConstraint(c: Context, lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition) { @@ -141,7 +141,7 @@ class ConstraintInjector( processConstraints(c, typeCheckerState, skipProperEqualityConstraints = false) } - fun processForkConstraints( + fun processGivenForkPointBranchConstraints( c: Context, constraintSet: Collection>, position: IncorporationConstraintPosition @@ -166,7 +166,7 @@ class ConstraintInjector( // During completion, we start processing fork constrains immediately if (c.atCompletionState) { - c.processForkConstraints() + c.resolveForkPointsConstraints() } } } @@ -275,8 +275,8 @@ class ConstraintInjector( private var possibleNewConstraints: MutableList>? = null private var forkPointsData: MutableList? = null - private var stackForConstraintsSetsFromCurrentForkPoint: Stack>? = null - private var stackForCurrentConstraintSetFromSingleFork: Stack>>? = null + private var stackForConstraintsSetsFromCurrentForkPoint: Stack>? = null + private var stackForConstraintSetFromCurrentForkPointBranch: Stack>>? = null override val isInferenceCompatibilityEnabled = languageVersionSettings.supportsFeature(LanguageFeature.InferenceCompatibility) @@ -294,9 +294,9 @@ class ConstraintInjector( fun addPossibleNewConstraint(variable: TypeVariableMarker, constraint: Constraint) { val constraintsSetsFromCurrentFork = stackForConstraintsSetsFromCurrentForkPoint?.lastOrNull() if (constraintsSetsFromCurrentFork != null) { - val currentConstraintSetForFork = stackForCurrentConstraintSetFromSingleFork?.lastOrNull() - require(currentConstraintSetForFork != null) { "Constraint has been added not under fork {...} call " } - currentConstraintSetForFork.add(variable to constraint) + val currentConstraintSetForForkPointBranch = stackForConstraintSetFromCurrentForkPointBranch?.lastOrNull() + require(currentConstraintSetForForkPointBranch != null) { "Constraint has been added not under fork {...} call " } + currentConstraintSetForForkPointBranch.add(variable to constraint) return } @@ -333,7 +333,7 @@ class ConstraintInjector( ) return true } else if (constraintSets.size == 1) { - processForkConstraints( + processGivenForkPointBranchConstraints( c, constraintSets.single(), position, @@ -347,17 +347,17 @@ class ConstraintInjector( var anyForkSuccessful = false override fun fork(block: () -> Boolean) { - if (stackForCurrentConstraintSetFromSingleFork == null) { - stackForCurrentConstraintSetFromSingleFork = SmartList() + if (stackForConstraintSetFromCurrentForkPointBranch == null) { + stackForConstraintSetFromCurrentForkPointBranch = SmartList() } - stackForCurrentConstraintSetFromSingleFork!!.add(SmartList()) + stackForConstraintSetFromCurrentForkPointBranch!!.add(SmartList()) block().also { anyForkSuccessful = anyForkSuccessful || it } stackForConstraintsSetsFromCurrentForkPoint!!.last() .addIfNotNull( - stackForCurrentConstraintSetFromSingleFork?.popLast()?.takeIf { it.isNotEmpty() }?.toSet() + stackForConstraintSetFromCurrentForkPointBranch?.popLast()?.takeIf { it.isNotEmpty() }?.toSet() ) } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt index cfde65da96e..0fda2b69957 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintSystemCompletionContext.kt @@ -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.model.PostponedAtomWithRevisableExpectedType 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 val allTypeVariables: Map @@ -33,7 +35,7 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex abstract fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>) abstract fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean - abstract fun processForkConstraints() + abstract fun resolveForkPointsConstraints() fun analyzeArgumentWithFixedParameterTypes( languageVersionSettings: LanguageVersionSettings, diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index e4b87c9c963..b324dbf9480 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -357,7 +357,11 @@ class NewConstraintSystemImpl( 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 val allForkPointsData = constraintsFromAllForkPoints.toList() 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) // 2. {Xv=T} – second constraints set for ((position, forkPointData) in allForkPointsData) { - if (!processForkPointData(forkPointData, position)) { + if (!applyConstraintsFromFirstSuccessfulBranchOfTheFork(forkPointData, position)) { addError(NoSuccessfulFork(position)) } } @@ -392,7 +396,7 @@ class NewConstraintSystemImpl( var result: ConstraintSystemError? = null runTransaction { for ((position, forkPointData) in allForkPointsData) { - if (!processForkPointData(forkPointData, position)) { + if (!applyConstraintsFromFirstSuccessfulBranchOfTheFork(forkPointData, position)) { result = NoSuccessfulFork(position) break } @@ -409,20 +413,20 @@ class NewConstraintSystemImpl( /** * @return true if there is a successful constraints set for the fork */ - private fun processForkPointData( + private fun applyConstraintsFromFirstSuccessfulBranchOfTheFork( forkPointData: ForkPointData, position: IncorporationConstraintPosition ): Boolean { - return forkPointData.any { constraintSetForSingleFork -> + return forkPointData.any { constraintSetForForkBranch -> runTransaction { - constraintInjector.processForkConstraints( + constraintInjector.processGivenForkPointBranchConstraints( this@NewConstraintSystemImpl.apply { checkState(State.BUILDING, State.COMPLETION, State.TRANSACTION) }, - constraintSetForSingleFork, + constraintSetForForkBranch, position, ) if (constraintsFromAllForkPoints.isNotEmpty()) { - processForkConstraints() + resolveForkPointsConstraints() } !hasContradiction