From 94bcf6d87f1f23e21efaee1d2204d8704c5ab9a1 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 1 Feb 2024 15:20:57 +0100 Subject: [PATCH] K2: handle recursive types properly in approximation This commit is intended to deal with inconsistency in K1/K2 star projection handling. K1 star projection includes a 'type' property. This type from a star projection can be used for relevant functions / properties return types, and already includes some approximation for recursive generics. In contrast, K2 star projection is an object, and return types of relevant functions / properties are represented as captured types. To prevent recursion in them in recursive generic case, this commit includes additional replacement of their type arguments. See more details in added comments. #KT-65057 Fixed --- .../kotlin/types/AbstractTypeApproximator.kt | 30 +++++++++++++- .../complexTypeUnwrapping.fir.txt | 4 +- .../overridingJKCases.fir.kt | 4 +- .../whereAndWithCases.fir.kt | 39 ------------------- .../whereAndWithCases.kt | 1 + .../ClashResolutionDescriptor.fir.ir.txt | 8 ++-- .../ClashResolutionDescriptor.fir.kt.txt | 2 +- 7 files changed, 38 insertions(+), 50 deletions(-) delete mode 100644 compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.fir.kt 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 bcc35da1e57..623cb94c23e 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/types/AbstractTypeApproximator.kt @@ -318,8 +318,32 @@ abstract class AbstractTypeApproximator( } } val baseSubType = type.lowerType() ?: nothingType() + // This replacement is important for resolving the code like below in K2. + // fun bar(y: FieldOrRef<*>) = y.field + // interface FieldOrRef> { val field: FF } + // abstract class AbstractField> + // During resolving the value parameter y type, K1 also builds a type for a star projection *. + // See fun TypeParameterDescriptor.starProjectionType(): KotlinType and fun buildStarProjectionTypeByTypeParameters. + // Thanks to it, K1 builds the star projection type as AbstractField<*> and no other approximation is needed. + // + // In turn, K2 never makes such a thing (K2 star projection has no associated type). + // Instead, it resolves y.field as CapturedType(*) C (see usage one line below v ), + // and the constructor of this captured type has a star projection and a supertype of AbstractField(C) + // Without this replacement, the type approximator currently cannot handle such a situation properly + // and builds AbstractField>>. + // The check it == type here is intended to find a recursion inside a captured type. + // A similar replacement for baseSubType looks unnecessary, no hits in the tests. + val replacedSuperType = if (isK2 && toSuper && baseSuperType.getArguments().any { it == type }) { + baseSuperType.replaceArguments { + // It's possible to use the stub here, because K2 star projection is an object and + // in fact this parameter is never used + if (it != type) it else createStarProjection(TypeParameterMarkerStubForK2StarProjection) + } + } else baseSuperType - val approximatedSuperType by lazy(LazyThreadSafetyMode.NONE) { approximateToSuperType(baseSuperType, conf, depth) } + val approximatedSuperType by lazy(LazyThreadSafetyMode.NONE) { + approximateToSuperType(replacedSuperType, conf, depth) + } val approximatedSubType by lazy(LazyThreadSafetyMode.NONE) { approximateToSubType(baseSubType, conf, depth) } if (!conf.capturedType(ctx, type)) { @@ -336,7 +360,7 @@ abstract class AbstractTypeApproximator( return null } } - val baseResult = if (toSuper) approximatedSuperType ?: baseSuperType else approximatedSubType ?: baseSubType + val baseResult = if (toSuper) approximatedSuperType ?: replacedSuperType else approximatedSubType ?: baseSubType // C = in Int, Int <: C => Int? <: C? // C = out Number, C <: Number => C? <: Number? @@ -359,6 +383,8 @@ abstract class AbstractTypeApproximator( } } + private object TypeParameterMarkerStubForK2StarProjection : TypeParameterMarker + private fun approximateSimpleToSuperType(type: SimpleTypeMarker, conf: TypeApproximatorConfiguration, depth: Int) = approximateTo(type, conf, toSuper = true, depth = depth) diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/complexTypeUnwrapping.fir.txt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/complexTypeUnwrapping.fir.txt index 9dc69b8f4bf..52c97bad9a0 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/complexTypeUnwrapping.fir.txt +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/complexTypeUnwrapping.fir.txt @@ -16,7 +16,7 @@ FILE: complexTypeUnwrapping.kt public get(): R|RE| } - public final fun foo(x: R|ElementOrRef<*, *>|): R|AbstractElement, out AbstractField>, out AbstractField>>, out AbstractField>>>, out AbstractField>>>>| { + public final fun foo(x: R|ElementOrRef<*, *>|): R|AbstractElement<*, out AbstractField<*>>| { ^foo R|/x|.R|SubstitutionOverride| } public abstract interface FieldOrRef|> : R|kotlin/Any| { @@ -24,6 +24,6 @@ FILE: complexTypeUnwrapping.kt public get(): R|FF| } - public final fun bar(y: R|FieldOrRef<*>|): R|AbstractField>>>>| { + public final fun bar(y: R|FieldOrRef<*>|): R|AbstractField<*>| { ^bar R|/y|.R|SubstitutionOverride| } diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/overridingJKCases.fir.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/overridingJKCases.fir.kt index f929a0aa00f..bfbcc420768 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/overridingJKCases.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/overridingJKCases.fir.kt @@ -37,9 +37,9 @@ fun javaInterfaceTest(a: A1<*>, b: B1<*>, c: C1<*>) { val x = a.foo() ")!>x val y = b.foo() - >>>>")!>y + ")!>y val z = c.foo() - >>>>..I>>>>?!")!>z + ..I<*>?!")!>z } fun javaClassTest(a: A2<*>, b: B2<*>, c: C2<*>, d: D2<*>) { diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.fir.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.fir.kt deleted file mode 100644 index d6039779375..00000000000 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.fir.kt +++ /dev/null @@ -1,39 +0,0 @@ -// ISSUE: KT-59012 -// !LANGUAGE: +TypeInferenceOnCallsWithSelfTypes - -interface I1> { - fun foo() : T - fun bar(vararg args: T) -} - -interface I2> { - fun foo() : T - fun bar(vararg args: T) -} - -interface I3 where G : I3 { - fun foo() : T where T : G - fun bar(vararg args: T) where T : G -} - -fun test(a: I2<*>, b: I3<*>) { - val x = a.foo() - ")!>x - a.bar() - val y = b.foo() - ")!>y - b.bar() -} - -fun withTest(a: I1<*>, b: I3<*>) { - with(a) { - val x = foo() - ")!>x - bar() - } - with(b) { - val y = foo() - ")!>y - bar() - } -} diff --git a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.kt b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.kt index fc8347b58a7..05f9c35f711 100644 --- a/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.kt +++ b/compiler/testData/diagnostics/tests/inference/recursiveTypes/selfTypes/enabledInferenceOnSelfTypes/whereAndWithCases.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // ISSUE: KT-59012 // !LANGUAGE: +TypeInferenceOnCallsWithSelfTypes diff --git a/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.ir.txt b/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.ir.txt index f0a9867e6ad..a9085feb72c 100644 --- a/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.ir.txt +++ b/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.ir.txt @@ -155,7 +155,7 @@ FILE fqName: fileName:/ClashResolutionDescriptor.kt TYPE_OP type=kotlin.collections.Collection<.ComponentDescriptor>? origin=SAFE_CAST typeOperand=kotlin.collections.Collection<.ComponentDescriptor> CALL 'public open fun get (p0: @[EnhancedNullability] K of java.util.HashMap): V of java.util.HashMap? declared in java.util.HashMap' type=kotlin.Any? origin=GET_ARRAY_ELEMENT $this: CALL 'private final fun (): java.util.HashMap declared in ' type=java.util.HashMap origin=GET_PROPERTY - p0: CALL 'public final fun (): java.lang.Class.PlatformExtensionsClashResolver> declared in .PlatformExtensionsClashResolver' type=java.lang.Class.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension>>>>> origin=GET_PROPERTY + p0: CALL 'public final fun (): java.lang.Class.PlatformExtensionsClashResolver> declared in .PlatformExtensionsClashResolver' type=java.lang.Class.PlatformSpecificExtension<*>> origin=GET_PROPERTY $this: GET_VAR 'val resolver: .PlatformExtensionsClashResolver<*> declared in .resolveClashesIfAny' type=.PlatformExtensionsClashResolver<*> origin=null WHEN type=kotlin.collections.Collection<.ComponentDescriptor> origin=null BRANCH @@ -166,9 +166,9 @@ FILE fqName: fileName:/ClashResolutionDescriptor.kt BRANCH if: CONST Boolean type=kotlin.Boolean value=true then: GET_VAR 'val tmp_1: kotlin.collections.Collection<.ComponentDescriptor>? declared in .resolveClashesIfAny' type=kotlin.collections.Collection<.ComponentDescriptor>? origin=null - VAR name:substituteDescriptor type:.ClashResolutionDescriptor.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension>>>>> [val] - CONSTRUCTOR_CALL 'public constructor (container: .ComponentContainer, resolver: .PlatformExtensionsClashResolver.ClashResolutionDescriptor>, clashedComponents: kotlin.collections.List<.ComponentDescriptor>) declared in .ClashResolutionDescriptor' type=.ClashResolutionDescriptor.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension>>>>> origin=null - : .PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension.PlatformSpecificExtension>>>> + VAR name:substituteDescriptor type:.ClashResolutionDescriptor.PlatformSpecificExtension<*>> [val] + CONSTRUCTOR_CALL 'public constructor (container: .ComponentContainer, resolver: .PlatformExtensionsClashResolver.ClashResolutionDescriptor>, clashedComponents: kotlin.collections.List<.ComponentDescriptor>) declared in .ClashResolutionDescriptor' type=.ClashResolutionDescriptor.PlatformSpecificExtension<*>> origin=null + : .PlatformSpecificExtension<*> container: GET_VAR 'container: .ComponentContainer declared in .resolveClashesIfAny' type=.ComponentContainer origin=null resolver: GET_VAR 'val resolver: .PlatformExtensionsClashResolver<*> declared in .resolveClashesIfAny' type=.PlatformExtensionsClashResolver<*> origin=null clashedComponents: CALL 'public final fun toList (): kotlin.collections.List declared in kotlin.collections' type=kotlin.collections.List<.ComponentDescriptor> origin=null diff --git a/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.kt.txt b/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.kt.txt index ccdec7208a0..b2ca4fbc02b 100644 --- a/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.kt.txt +++ b/compiler/testData/ir/irText/firProblems/ClashResolutionDescriptor.fir.kt.txt @@ -54,7 +54,7 @@ fun resolveClashesIfAny(container: ComponentContainer, clashResolvers: List tmp_1 } } - val substituteDescriptor: ClashResolutionDescriptor>>>>> = ClashResolutionDescriptor>>>>>(container = container, resolver = resolver, clashedComponents = clashedComponents.toList()) + val substituteDescriptor: ClashResolutionDescriptor> = ClashResolutionDescriptor>(container = container, resolver = resolver, clashedComponents = clashedComponents.toList()) } } }