diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt index d85d25420fa..fad22e07e47 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrTypeConverter.kt @@ -58,7 +58,11 @@ class Fir2IrTypeConverter( StandardClassIds.Char to irBuiltIns.charType ) + private val capturedTypeCache = mutableMapOf() + private val capturedTypeStack = mutableSetOf() + fun FirTypeRef.toIrType(typeContext: ConversionTypeContext = ConversionTypeContext.DEFAULT): IrType { + capturedTypeStack.clear() return when (this) { !is FirResolvedTypeRef -> createErrorType() !is FirImplicitBuiltinTypeRef -> type.toIrType(typeContext, annotations) @@ -106,7 +110,16 @@ class Fir2IrTypeConverter( upperBound.toIrType(typeContext) } is ConeCapturedType -> { - lowerType?.toIrType(typeContext) ?: constructor.supertypes!!.first().toIrType(typeContext) + if (capturedTypeStack.add(this)) { + val irType = lowerType?.toIrType(typeContext) ?: constructor.supertypes!!.first().toIrType(typeContext) + capturedTypeCache[this] = irType + irType + } else { + // Potentially recursive captured type, e.g., Recursive where R : Recursive, ... + // That should have been handled during type argument conversion, though. + // Or, simply repeated captured type, e.g., FunctionN<..., *, ..., *>, literally same captured types. + capturedTypeCache[this] ?: createErrorType() + } } is ConeDefinitelyNotNullType -> { original.toIrType(typeContext.definitelyNotNull()) @@ -132,12 +145,48 @@ class Fir2IrTypeConverter( makeTypeProjection(irType, Variance.OUT_VARIANCE) } is ConeKotlinType -> { - val irType = toIrType(typeContext) - makeTypeProjection(irType, Variance.INVARIANT) + if (this is ConeCapturedType && capturedTypeStack.contains(this) && this.isRecursive(mutableSetOf())) { + // Recursive captured type, e.g., Recursive where R : Recursive, ... + // We can return * early here to avoid recursive type conversions. + IrStarProjectionImpl + } else { + val irType = toIrType(typeContext) + makeTypeProjection(irType, Variance.INVARIANT) + } } } } + private fun ConeKotlinType.isRecursive(visited: MutableSet): Boolean = + when (this) { + is ConeLookupTagBasedType -> { + typeArguments.any { + when (it) { + is ConeKotlinType -> it.isRecursive(visited) + is ConeKotlinTypeProjectionIn -> it.type.isRecursive(visited) + is ConeKotlinTypeProjectionOut -> it.type.isRecursive(visited) + else -> false + } + } + } + is ConeFlexibleType -> { + lowerBound.isRecursive(visited) || upperBound.isRecursive(visited) + } + is ConeCapturedType -> { + if (visited.add(this)) { + constructor.supertypes?.any { it.isRecursive(visited) } == true + } else + true + } + is ConeDefinitelyNotNullType -> { + original.isRecursive(visited) + } + is ConeIntersectionType -> { + intersectedTypes.any { it.isRecursive(visited) } + } + else -> false + } + private fun getArrayClassSymbol(classId: ClassId?): IrClassSymbol? { val primitiveId = StandardClassIds.elementTypeByPrimitiveArrayType[classId] ?: return null val irType = classIdToTypeMap[primitiveId] diff --git a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/fir/Fir2IrTextTestGenerated.java b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/fir/Fir2IrTextTestGenerated.java index d2b5eacc7f3..6b5e0c36331 100644 --- a/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/fir/Fir2IrTextTestGenerated.java +++ b/compiler/fir/fir2ir/tests/org/jetbrains/kotlin/fir/Fir2IrTextTestGenerated.java @@ -1807,6 +1807,11 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest { runTest("compiler/testData/ir/irText/firProblems/putIfAbsent.kt"); } + @TestMetadata("recursiveCapturedTypeInPropertyReference.kt") + public void testRecursiveCapturedTypeInPropertyReference() throws Exception { + runTest("compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.kt"); + } + @TestMetadata("SameJavaFieldReferences.kt") public void testSameJavaFieldReferences() throws Exception { runTest("compiler/testData/ir/irText/firProblems/SameJavaFieldReferences.kt"); diff --git a/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.fir.txt b/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.fir.txt new file mode 100644 index 00000000000..23c851dd3b6 --- /dev/null +++ b/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.fir.txt @@ -0,0 +1,68 @@ +FILE fqName: fileName:/recursiveCapturedTypeInPropertyReference.kt + CLASS INTERFACE name:Something modality:ABSTRACT visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.Something + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS INTERFACE name:Recursive modality:ABSTRACT visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.Recursive.Recursive> + TYPE_PARAMETER name:R index:0 variance: superTypes:[.Recursive.Recursive>; .Something] + PROPERTY name:symbol visibility:public modality:ABSTRACT [val] + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:ABSTRACT <> ($this:.Recursive.Recursive>) returnType:.AbstractSymbol.Recursive> + correspondingProperty: PROPERTY name:symbol visibility:public modality:ABSTRACT [val] + $this: VALUE_PARAMETER name: type:.Recursive.Recursive> + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS CLASS name:AbstractSymbol modality:ABSTRACT visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.AbstractSymbol.AbstractSymbol> + TYPE_PARAMETER name:E index:0 variance: superTypes:[.Recursive.AbstractSymbol>; .Something] + CONSTRUCTOR visibility:public <> () returnType:.AbstractSymbol.AbstractSymbol> [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:AbstractSymbol modality:ABSTRACT visibility:public superTypes:[kotlin.Any]' + FUN name:foo visibility:public modality:FINAL <> ($this:.AbstractSymbol.AbstractSymbol>, list:kotlin.collections.List) returnType:kotlin.Unit + $this: VALUE_PARAMETER name: type:.AbstractSymbol.AbstractSymbol> + VALUE_PARAMETER name:list index:0 type:kotlin.collections.List + BLOCK_BODY + VAR name:result type:kotlin.collections.List<.AbstractSymbol.Recursive<*>>> [val] + CALL 'public final fun map (transform: kotlin.Function1): kotlin.collections.List [inline] declared in kotlin.collections' type=kotlin.collections.List<.AbstractSymbol.Recursive<*>>> origin=null + : .Recursive<*> + : .AbstractSymbol.Recursive<*>> + $receiver: CALL 'public final fun filterIsInstance (): kotlin.collections.List [inline] declared in kotlin.collections' type=kotlin.collections.List<.Recursive<*>> origin=null + : .Recursive<*> + $receiver: GET_VAR 'list: kotlin.collections.List declared in .AbstractSymbol.foo' type=kotlin.collections.List origin=null + transform: PROPERTY_REFERENCE 'public abstract symbol: .AbstractSymbol.Recursive> [val]' field=null getter='public abstract fun (): .AbstractSymbol.Recursive> declared in .Recursive' setter=null type=kotlin.reflect.KProperty1<.Recursive<*>, .AbstractSymbol<.Recursive<*>>> origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any diff --git a/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.kt b/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.kt new file mode 100644 index 00000000000..7b7dd6d6098 --- /dev/null +++ b/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.kt @@ -0,0 +1,14 @@ +// WITH_RUNTIME +// FULL_JDK + +interface Something + +interface Recursive where R : Recursive, R : Something { + val symbol: AbstractSymbol +} + +abstract class AbstractSymbol where E : Recursive, E : Something { + fun foo(list: List) { + val result = list.filterIsInstance>().map(Recursive<*>::symbol) + } +} diff --git a/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.txt b/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.txt new file mode 100644 index 00000000000..ebfe0eed446 --- /dev/null +++ b/compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.txt @@ -0,0 +1,68 @@ +FILE fqName: fileName:/recursiveCapturedTypeInPropertyReference.kt + CLASS INTERFACE name:Something modality:ABSTRACT visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.Something + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS INTERFACE name:Recursive modality:ABSTRACT visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.Recursive.Recursive> + TYPE_PARAMETER name:R index:0 variance: superTypes:[.Recursive.Recursive>; .Something] + PROPERTY name:symbol visibility:public modality:ABSTRACT [val] + FUN DEFAULT_PROPERTY_ACCESSOR name: visibility:public modality:ABSTRACT <> ($this:.Recursive.Recursive>) returnType:.AbstractSymbol.Recursive> + correspondingProperty: PROPERTY name:symbol visibility:public modality:ABSTRACT [val] + $this: VALUE_PARAMETER name: type:.Recursive.Recursive> + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + CLASS CLASS name:AbstractSymbol modality:ABSTRACT visibility:public superTypes:[kotlin.Any] + $this: VALUE_PARAMETER INSTANCE_RECEIVER name: type:.AbstractSymbol.AbstractSymbol> + TYPE_PARAMETER name:E index:0 variance: superTypes:[.Recursive.AbstractSymbol>; .Something] + CONSTRUCTOR visibility:public <> () returnType:.AbstractSymbol.AbstractSymbol> [primary] + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'public constructor () [primary] declared in kotlin.Any' + INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:AbstractSymbol modality:ABSTRACT visibility:public superTypes:[kotlin.Any]' + FUN name:foo visibility:public modality:FINAL <> ($this:.AbstractSymbol.AbstractSymbol>, list:kotlin.collections.List) returnType:kotlin.Unit + $this: VALUE_PARAMETER name: type:.AbstractSymbol.AbstractSymbol> + VALUE_PARAMETER name:list index:0 type:kotlin.collections.List + BLOCK_BODY + VAR name:result type:kotlin.collections.List<.AbstractSymbol.Recursive<*>>> [val] + CALL 'public final fun map (transform: kotlin.Function1): kotlin.collections.List [inline] declared in kotlin.collections' type=kotlin.collections.List<.AbstractSymbol.Recursive<*>>> origin=null + : .Recursive<*> + : .AbstractSymbol.Recursive<*>> + $receiver: CALL 'public final fun filterIsInstance (): kotlin.collections.List<@[NoInfer] R of kotlin.collections.filterIsInstance> [inline] declared in kotlin.collections' type=kotlin.collections.List<@[NoInfer] .Recursive<*>> origin=null + : .Recursive<*> + $receiver: GET_VAR 'list: kotlin.collections.List declared in .AbstractSymbol.foo' type=kotlin.collections.List origin=null + transform: PROPERTY_REFERENCE 'public abstract symbol: .AbstractSymbol.Recursive> [val]' field=null getter='public abstract fun (): .AbstractSymbol.Recursive> declared in .Recursive' setter=null type=kotlin.reflect.KProperty1<.Recursive<*>, .AbstractSymbol.Recursive<*>>> origin=null + FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator] + overridden: + public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + VALUE_PARAMETER name:other index:0 type:kotlin.Any? + FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override] + overridden: + public open fun hashCode (): kotlin.Int declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any + FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override] + overridden: + public open fun toString (): kotlin.String declared in kotlin.Any + $this: VALUE_PARAMETER name: type:kotlin.Any diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 88d73c7f06d..a88717f6883 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -1806,6 +1806,11 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { runTest("compiler/testData/ir/irText/firProblems/putIfAbsent.kt"); } + @TestMetadata("recursiveCapturedTypeInPropertyReference.kt") + public void testRecursiveCapturedTypeInPropertyReference() throws Exception { + runTest("compiler/testData/ir/irText/firProblems/recursiveCapturedTypeInPropertyReference.kt"); + } + @TestMetadata("SameJavaFieldReferences.kt") public void testSameJavaFieldReferences() throws Exception { runTest("compiler/testData/ir/irText/firProblems/SameJavaFieldReferences.kt");