NI: Support forking inference with heuristics

Mostly, it only affects FIR

It partially allows to consider several variance of constraints like
A<Int> & A<T> <: A<X_var> that are mostly brought by smart casts

^KT-49542 Fixed
^KT-50489 Relates
This commit is contained in:
Denis.Zharkov
2021-12-24 14:24:19 +03:00
committed by teamcity
parent 62673c7e1b
commit a33d9df0cd
28 changed files with 401 additions and 11 deletions
@@ -62,6 +62,34 @@ open class TypeCheckerState(
isFromNullabilityConstraint: Boolean = false
): Boolean? = null
// Handling cases like A<Int> & A<T> <: A<F_var>
// There are two possible solutions for F_var (Int and T) and both of them may work well or not with other constrains
// Effectively, we need to fork constraint system to two copies: one with F_var=Int and the other with F_var=T
// and then maintain them both until we find some contradiction with one of the versions.
//
// But that might lead to the exponential size of CS, thus we use the following heuristics:
// we accumulate forks data until the last stage of the candidate resolution and then try to apply back then
// until some of the constrains set has no contradiction.
//
// `atForkPoint` works trivially in non-inference context and for FE1.0: it just run basic subtyping mechanism for each subTypeArguments
// component until the first success
open fun runForkingPoint(block: ForkPointContext.() -> Unit): Boolean = with(ForkPointContext.Default()) {
block()
result
}
interface ForkPointContext {
fun fork(block: () -> Boolean)
class Default : ForkPointContext {
var result: Boolean = false
override fun fork(block: () -> Boolean) {
if (result) return
result = block()
}
}
}
enum class LowerCapturedTypePolicy {
CHECK_ONLY_LOWER,
CHECK_SUBTYPE_AND_LOWER,
@@ -362,8 +390,11 @@ object AbstractTypeChecker {
if (!anyNonOutParameter && state.isSubtypeForSameConstructor(newArguments, superType)) return true
// TODO: rethink this; now components order in intersection type affects semantic due to run subtyping (which can add constraints) only until the first successful candidate
return supertypesWithSameConstructor.any { state.isSubtypeForSameConstructor(it.asArgumentList(), superType) }
return state.runForkingPoint {
for (subTypeArguments in supertypesWithSameConstructor) {
fork { state.isSubtypeForSameConstructor(subTypeArguments.asArgumentList(), superType) }
}
}
}
}
}