[FIR] Don't approximate captured types

This fixes some type argument mismatch errors caused by a captured type
being approximated and then captured again.
Some places need to be adapted to work with captured types that
previously only worked with approximated types.

#KT-62959 Fixed
This commit is contained in:
Kirill Rakhman
2023-12-20 11:10:25 +01:00
committed by Space Team
parent b6d7f35ebf
commit 251827c9aa
58 changed files with 506 additions and 86 deletions
@@ -32,7 +32,12 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
// mutable operations
abstract fun addError(error: ConstraintSystemError)
abstract fun fixVariable(variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>)
abstract fun fixVariable(
variable: TypeVariableMarker,
resultType: KotlinTypeMarker,
position: FixVariableConstraintPosition<*>,
resultTypeForOnlyInputTypes: KotlinTypeMarker = resultType
)
abstract fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean
abstract fun resolveForkPointsConstraints()
@@ -58,8 +58,8 @@ class ResultTypeResolver(
return if (direction == ResolveDirection.TO_SUBTYPE) nothingType() else nullableAnyType()
}
fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): KotlinTypeMarker {
findResultTypeOrNull(c, variableWithConstraints, direction)?.let { return it }
fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection, isForOnlyInputTypes: Boolean = false): KotlinTypeMarker {
findResultTypeOrNull(c, variableWithConstraints, direction, isForOnlyInputTypes)?.let { return it }
// no proper constraints
return c.getDefaultType(direction, variableWithConstraints.constraints, variableWithConstraints.typeVariable)
@@ -68,7 +68,8 @@ class ResultTypeResolver(
fun findResultTypeOrNull(
c: Context,
variableWithConstraints: VariableWithConstraints,
direction: ResolveDirection
direction: ResolveDirection,
isForOnlyInputTypes: Boolean = false,
): KotlinTypeMarker? {
val resultTypeFromEqualConstraint = findResultIfThereIsEqualsConstraint(c, variableWithConstraints)
if (resultTypeFromEqualConstraint != null) {
@@ -83,7 +84,7 @@ class ResultTypeResolver(
}
}
val subType = c.findSubType(variableWithConstraints)
val subType = c.findSubType(variableWithConstraints, isForOnlyInputTypes)
// Super type should be the most flexible, sub type should be the least one
val superType = c.findSuperType(variableWithConstraints).makeFlexibleIfNecessary(c, variableWithConstraints.constraints)
@@ -208,8 +209,8 @@ class ResultTypeResolver(
return constraints.singleOrNull { it.kind.isLower() }?.isNullabilityConstraint ?: false
}
private fun Context.findSubType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? {
val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints)
private fun Context.findSubType(variableWithConstraints: VariableWithConstraints, isForOnlyInputTypes: Boolean): KotlinTypeMarker? {
val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints, isForOnlyInputTypes)
if (lowerConstraintTypes.isNotEmpty()) {
val types = sinkIntegerLiteralTypes(lowerConstraintTypes)
@@ -259,7 +260,7 @@ class ResultTypeResolver(
private fun Context.computeCommonSuperType(types: List<KotlinTypeMarker>): KotlinTypeMarker =
with(NewCommonSuperTypeCalculator) { commonSuperType(types) }
private fun Context.prepareLowerConstraints(constraints: List<Constraint>): List<KotlinTypeMarker> {
private fun Context.prepareLowerConstraints(constraints: List<Constraint>, isForOnlyInputTypes: Boolean): List<KotlinTypeMarker> {
var atLeastOneProper = false
var atLeastOneNonProper = false
@@ -268,7 +269,15 @@ class ResultTypeResolver(
for (constraint in constraints) {
if (constraint.kind != ConstraintKind.LOWER) continue
val type = constraint.type
var type = constraint.type
// The call to commonSuperTypes will replace arguments that have recursive supertypes with star projections,
// which will lead to a different resulting type than approximating it first.
// This breaks the @OnlyInputTypes check for captured types with recursive super types.
if (isForOnlyInputTypes && isK2 && hasRecursiveTypeParametersWithGivenSelfType(type.typeConstructor())) {
type = typeApproximator.approximateToSuperType(type, TypeApproximatorConfiguration.InternalTypesApproximation) ?: type
}
lowerConstraintTypes.add(type)
if (isProperTypeForFixation(type)) {
@@ -567,6 +567,7 @@ class NewConstraintSystemImpl(
variable: TypeVariableMarker,
resultType: KotlinTypeMarker,
position: FixVariableConstraintPosition<*>,
resultTypeForOnlyInputTypes: KotlinTypeMarker,
) = with(utilContext) {
checkState(State.BUILDING, State.COMPLETION)
@@ -593,7 +594,7 @@ class NewConstraintSystemImpl(
// Substitute freshly fixed type variable into missed constraints
substituteMissedConstraints()
postponeOnlyInputTypesCheck(variableWithConstraints, resultType)
postponeOnlyInputTypesCheck(variableWithConstraints, resultTypeForOnlyInputTypes)
doPostponedComputationsIfAllVariablesAreFixed()
}
@@ -113,6 +113,24 @@ open class TypeApproximatorConfiguration {
override val convertToNonRawVersionAfterApproximationInK2: Boolean get() = true
}
object IntermediateApproximationToSupertypeAfterCompletionInK2 :
AbstractCapturedTypesApproximation(null) {
override val integerLiteralConstantType: Boolean get() = true
override val integerConstantOperatorType: Boolean get() = true
override val intersectionTypesInContravariantPositions: Boolean get() = true
override val convertToNonRawVersionAfterApproximationInK2: Boolean get() = true
override fun capturedType(ctx: TypeSystemInferenceExtensionContext, type: CapturedTypeMarker): Boolean {
/**
* Only approximate captured types when they contain a raw supertype.
* This is an awful hack required to keep K1 compatibility.
* See [convertToNonRawVersionAfterApproximationInK2].
*/
return type.captureStatus(ctx) == CaptureStatus.FROM_EXPRESSION && with(ctx) { type.hasRawSuperType() }
}
}
object TypeArgumentApproximation : AbstractCapturedTypesApproximation(null) {
override val integerLiteralConstantType: Boolean get() = true
override val integerConstantOperatorType: Boolean get() = true