diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.fir.txt b/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.fir.txt index 3c0f51a20a4..9647d3cc12a 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.fir.txt @@ -28,6 +28,8 @@ FILE: onlyInputTypesCapturedTypeWithRecursiveBounds.kt } private final fun test(node: R|Foo<*>|): R|kotlin/Unit| { R|/node|.R|SubstitutionOverride|>|.R|kotlin/collections/get| & I)|, R|kotlin/Any?|>(Q|MyEnum|.R|/MyEnum.EnumEntry|) + lval map: R|kotlin/collections/Map & I), kotlin/Any?>| = R|/node|.R|SubstitutionOverride|>| + R|/map|.R|kotlin/collections/get| & I)|, R|kotlin/Any?|>(Q|MyEnum|.R|/MyEnum.EnumEntry|) } public open class FooEnumMap

|, R|I|> : R|kotlin/Any| { public constructor

|, R|I|>(data: R|java/util/EnumMap|): R|FooEnumMap

| { @@ -40,6 +42,8 @@ FILE: onlyInputTypesCapturedTypeWithRecursiveBounds.kt } private final fun test(node: R|FooEnumMap<*>|): R|kotlin/Unit| { R|/node|.R|SubstitutionOverride|>|.R|kotlin/collections/get| & I), it(kotlin/Enum<*>? & I?)>|, R|kotlin/Any!|>(Q|MyEnum|.R|/MyEnum.EnumEntry|) + lval map: R|java/util/EnumMap & I), kotlin/Any?>| = R|/node|.R|SubstitutionOverride|>| + R|/map|.R|kotlin/collections/get| & I), it(kotlin/Enum<*>? & I?)>> & I), it(kotlin/Enum<*>? & I?)>> & I), it(kotlin/Enum<*>? & I?)>|, R|kotlin/Any!|>(Q|MyEnum|.R|/MyEnum.EnumEntry|) } public open class Foo2

: R|kotlin/Any| { public constructor

(data: R|kotlin/collections/Map|): R|Foo2

| { @@ -52,6 +56,8 @@ FILE: onlyInputTypesCapturedTypeWithRecursiveBounds.kt } private final fun test(node: R|Foo2<*>|): R|kotlin/Unit| { R|/node|.R|SubstitutionOverride|>|.R|kotlin/collections/get|(Q|MyEnum|.R|/MyEnum.EnumEntry|) + lval map: R|kotlin/collections/Map| = R|/node|.R|SubstitutionOverride|>| + R|/map|.R|kotlin/collections/get|(Q|MyEnum|.R|/MyEnum.EnumEntry|) } public abstract interface I2 : R|kotlin/Any| { } diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.kt b/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.kt index 83fcab84804..6d564123bb4 100644 --- a/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.kt +++ b/compiler/fir/analysis-tests/testData/resolve/inference/onlyInputTypesCapturedTypeWithRecursiveBounds.kt @@ -12,18 +12,26 @@ open class Foo

(val data: Map) where P : Enum

, P : I private fun test(node: Foo<*>) { node.data.get(MyEnum.EnumEntry) + val map = node.data + map.get(MyEnum.EnumEntry) } open class FooEnumMap

(val data: EnumMap) where P : Enum

, P : I private fun test(node: FooEnumMap<*>) { node.data.get(MyEnum.EnumEntry) + val map = node.data + // TODO inferred type argument is giant multi-level type KT-65704 + // R|/map|.R|kotlin/collections/get| & I), it(kotlin/Enum<*>? & I?)>> & I), it(kotlin/Enum<*>? & I?)>> & I), it(kotlin/Enum<*>? & I?)>|, R|kotlin/Any!|>(Q|MyEnum|.R|/MyEnum.EnumEntry|) + map.get(MyEnum.EnumEntry) } open class Foo2

(val data: Map) private fun test(node: Foo2<*>) { node.data.get(MyEnum.EnumEntry) + val map = node.data + map.get(MyEnum.EnumEntry) } interface I2 @@ -34,4 +42,5 @@ open class Foo3

(val data: Map) where P : I, P : I2 fun test3(node: Foo3<*>) { node.data.get(C) -} \ No newline at end of file +} + 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 03ae915afd3..2cba8e31a3e 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 @@ -419,20 +419,8 @@ class ConstraintSystemCompleter(components: BodyResolveComponents) { TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN ) - val resultTypeForOnlyInputTypes = - if (with(ConeConstraintSystemUtilContext) { variableWithConstraints.typeVariable.hasOnlyInputTypesAttribute() }) { - inferenceComponents.resultTypeResolver.findResultType( - c, - variableWithConstraints, - TypeVariableDirectionCalculator.ResolveDirection.UNKNOWN, - isForOnlyInputTypes = true - ) - } else { - resultType - } - val variable = variableWithConstraints.typeVariable - c.fixVariable(variable, resultType, ConeFixVariableConstraintPosition(variable), resultTypeForOnlyInputTypes) + c.fixVariable(variable, resultType, ConeFixVariableConstraintPosition(variable)) } companion object { diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt index 7a01ed6af9d..62a9d178d29 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/NewCommonSuperTypeCalculator.kt @@ -401,7 +401,7 @@ object NewCommonSuperTypeCalculator { return false // arguments for contravariant parameters are intersected, recursion should not be possible val originalTypesSet = originalTypesForCst.toSet() - val typeArgumentsTypeSet = typeArgumentsForSuperConstructorParameter.map { it.getType().lowerBoundIfFlexible() }.toSet() + val typeArgumentsTypeSet = typeArgumentsForSuperConstructorParameter.map { it.getType().lowerBoundIfFlexible().originalIfDefinitelyNotNullable() }.toSet() if (originalTypesSet.size != typeArgumentsTypeSet.size) return false @@ -414,7 +414,7 @@ object NewCommonSuperTypeCalculator { var starProjectionFound = false for (supertype in supertypesIfCapturedStarProjection(argumentType).orEmpty()) { - if (supertype.lowerBoundIfFlexible().typeConstructor() !in originalTypeConstructorSet) + if (supertype.lowerBoundIfFlexible().originalIfDefinitelyNotNullable().typeConstructor() !in originalTypeConstructorSet) return false else starProjectionFound = true } @@ -429,7 +429,7 @@ object NewCommonSuperTypeCalculator { for (type in types) { if (isCapturedStarProjection(type)) { for (supertype in supertypesIfCapturedStarProjection(type).orEmpty()) { - yield(supertype.lowerBoundIfFlexible().typeConstructor()) + yield(supertype.lowerBoundIfFlexible().originalIfDefinitelyNotNullable().typeConstructor()) } } else { yield(type.typeConstructor()) 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 605eba48e14..e01bccde6da 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 @@ -36,7 +36,6 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>, - resultTypeForOnlyInputTypes: KotlinTypeMarker = resultType ) abstract fun couldBeResolvedWithUnrestrictedBuilderInference(): Boolean diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt index ec5586bb1f6..be5fca9788f 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/components/ResultTypeResolver.kt @@ -59,8 +59,8 @@ class ResultTypeResolver( return if (direction == ResolveDirection.TO_SUBTYPE) nothingType() else nullableAnyType() } - fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection, isForOnlyInputTypes: Boolean = false): KotlinTypeMarker { - findResultTypeOrNull(c, variableWithConstraints, direction, isForOnlyInputTypes)?.let { return it } + fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): KotlinTypeMarker { + findResultTypeOrNull(c, variableWithConstraints, direction)?.let { return it } // no proper constraints return c.getDefaultType(direction, variableWithConstraints.constraints, variableWithConstraints.typeVariable) @@ -70,7 +70,6 @@ class ResultTypeResolver( c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection, - isForOnlyInputTypes: Boolean = false, ): KotlinTypeMarker? { val resultTypeFromEqualConstraint = findResultIfThereIsEqualsConstraint(c, variableWithConstraints) if (resultTypeFromEqualConstraint != null) { @@ -85,7 +84,7 @@ class ResultTypeResolver( } } - val subType = c.findSubType(variableWithConstraints, isForOnlyInputTypes) + val subType = c.findSubType(variableWithConstraints) // Super type should be the most flexible, sub type should be the least one val superType = c.findSuperType(variableWithConstraints).makeFlexibleIfNecessary(c, variableWithConstraints.constraints) @@ -210,11 +209,12 @@ class ResultTypeResolver( return constraints.singleOrNull { it.kind.isLower() }?.isNullabilityConstraint ?: false } - private fun Context.findSubType(variableWithConstraints: VariableWithConstraints, isForOnlyInputTypes: Boolean): KotlinTypeMarker? { - val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints, isForOnlyInputTypes) + private fun Context.findSubType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? { + val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints) if (lowerConstraintTypes.isNotEmpty()) { val types = sinkIntegerLiteralTypes(lowerConstraintTypes) + // TODO Improve handling of flexible types with recursive captured type arguments to not produce giant multi-level-deep types KT-65704 var commonSuperType = computeCommonSuperType(types) if (commonSuperType.contains { it.asSimpleType()?.isStubTypeForVariableInSubtyping() == true }) { @@ -268,7 +268,7 @@ class ResultTypeResolver( private fun Context.computeCommonSuperType(types: List): KotlinTypeMarker = with(NewCommonSuperTypeCalculator) { commonSuperType(types) } - private fun Context.prepareLowerConstraints(constraints: List, isForOnlyInputTypes: Boolean): List { + private fun Context.prepareLowerConstraints(constraints: List): List { var atLeastOneProper = false var atLeastOneNonProper = false @@ -277,14 +277,7 @@ class ResultTypeResolver( for (constraint in constraints) { if (constraint.kind != ConstraintKind.LOWER) continue - 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 - } + val type = constraint.type lowerConstraintTypes.add(type) 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 9c2189b17d5..6a2d162075f 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 @@ -567,7 +567,6 @@ class NewConstraintSystemImpl( variable: TypeVariableMarker, resultType: KotlinTypeMarker, position: FixVariableConstraintPosition<*>, - resultTypeForOnlyInputTypes: KotlinTypeMarker, ) = with(utilContext) { checkState(State.BUILDING, State.COMPLETION) @@ -594,7 +593,7 @@ class NewConstraintSystemImpl( // Substitute freshly fixed type variable into missed constraints substituteMissedConstraints() - postponeOnlyInputTypesCheck(variableWithConstraints, resultTypeForOnlyInputTypes) + postponeOnlyInputTypesCheck(variableWithConstraints, resultType) doPostponedComputationsIfAllVariablesAreFixed() } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt index 63fe48d1238..795d4ac0932 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt @@ -288,7 +288,7 @@ abstract class AbstractTypeApproximator( toSuper: Boolean, depth: Int ): KotlinTypeMarker? { - fun KotlinTypeMarker.replaceRecursionWithStarProjection(capturedType: CapturedTypeMarker, toSuper: Boolean): KotlinTypeMarker { + fun KotlinTypeMarker.replaceRecursionWithStarProjection(capturedType: CapturedTypeMarker): KotlinTypeMarker { // This replacement is important for resolving the code like below in K2. // fun bar(y: FieldOrRef<*>) = y.field // interface FieldOrRef> { val field: FF } @@ -311,7 +311,7 @@ abstract class AbstractTypeApproximator( return getType().lowerBoundIfFlexible().originalIfDefinitelyNotNullable() } - return if (isK2 && toSuper && getArguments().any { it.unwrapForComparison() == capturedType }) { + return if (isK2 && getArguments().any { it.unwrapForComparison() == capturedType }) { replaceArguments { when { it.unwrapForComparison() != capturedType -> it @@ -326,7 +326,7 @@ abstract class AbstractTypeApproximator( val supertypes = capturedType.typeConstructor().supertypes() val baseSuperType = when (supertypes.size) { 0 -> nullableAnyType() // Let C = in Int, then superType for C and C? is Any? - 1 -> supertypes.single().replaceRecursionWithStarProjection(capturedType, toSuper) + 1 -> supertypes.single().replaceRecursionWithStarProjection(capturedType) // Consider the following example: // A.getA()::class.java, where `getA()` returns some class from Java @@ -347,7 +347,7 @@ abstract class AbstractTypeApproximator( else -> { val projection = capturedType.typeConstructorProjection() - if (projection.isStarProjection()) intersectTypes(supertypes.map { it.replaceRecursionWithStarProjection(capturedType, toSuper) }) + if (projection.isStarProjection()) intersectTypes(supertypes.map { it.replaceRecursionWithStarProjection(capturedType) }) else projection.getType() } }