[Analysis API] Add recursive value classes handling to `AbstractTypeMapper`
Without this fix, calls to `isPrimitiveBacked` and `valueClassLoweringKind` on recursive value classes result in endless loops and SOE.
^KT-72227 fixed
diff --git a/analysis/analysis-api/testData/components/psiTypeProvider/psiType/forDeclaration/recursiveSingleValueClass.kt b/analysis/analysis-api/testData/components/psiTypeProvider/psiType/forDeclaration/recursiveSingleValueClass.kt
index a95a91b..1b2990e 100644
--- a/analysis/analysis-api/testData/components/psiTypeProvider/psiType/forDeclaration/recursiveSingleValueClass.kt
+++ b/analysis/analysis-api/testData/components/psiTypeProvider/psiType/forDeclaration/recursiveSingleValueClass.kt
@@ -1,5 +1,3 @@
-// IGNORE_FIR
-
package pack
@JvmInline
diff --git a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/types/AbstractTypeMapper.kt b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/types/AbstractTypeMapper.kt
index a803c7a..106aa86 100644
--- a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/types/AbstractTypeMapper.kt
+++ b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/types/AbstractTypeMapper.kt
@@ -221,8 +221,22 @@
type: KotlinTypeMarker
): Boolean = context.typeContext.isPrimitiveBacked(type)
- private fun TypeSystemCommonBackendContext.isPrimitiveBacked(type: KotlinTypeMarker): Boolean =
- !type.isNullableType() &&
- (type is SimpleTypeMarker && type.isPrimitiveType() ||
- type.typeConstructor().getValueClassProperties()?.singleOrNull()?.let { isPrimitiveBacked(it.second) } == true)
+ private fun TypeSystemCommonBackendContext.isPrimitiveBacked(
+ type: KotlinTypeMarker,
+ visited: HashSet<TypeConstructorMarker> = hashSetOf()
+ ): Boolean {
+ if (type.isNullableType()) {
+ return false
+ }
+
+ if (type is SimpleTypeMarker && type.isPrimitiveType()) {
+ return true
+ }
+
+ val typeConstructor = type.typeConstructor()
+
+ return visited.add(typeConstructor) &&
+ typeConstructor.getValueClassProperties()?.singleOrNull()
+ ?.let { isPrimitiveBacked(it.second, visited) } == true
+ }
}
diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt
index 05af692..4daba93 100644
--- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt
+++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/types/ConeTypeContext.kt
@@ -527,7 +527,21 @@
override fun TypeConstructorMarker.isMultiFieldValueClass(): Boolean {
val fields = getValueClassProperties() ?: return false
- return this@ConeTypeContext.valueClassLoweringKind(fields) == ValueClassKind.MultiField
+ return isMultiFieldValueClassRecursionAware(fields, visited = hashSetOf())
+ }
+
+ private fun TypeConstructorMarker.isMultiFieldValueClassRecursionAware(
+ fields: List<Pair<Name, RigidTypeMarker>>,
+ visited: MutableSet<TypeConstructorMarker>,
+ ): Boolean {
+ if (fields.size > 1) return true
+ val fieldType = fields.singleOrNull()?.second ?: return false
+ if (!visited.add(this)) return false
+
+ val typeConstructor = fieldType.typeConstructor()
+ return !fieldType.isNullableType() && typeConstructor.getValueClassProperties()?.let {
+ typeConstructor.isMultiFieldValueClassRecursionAware(it, visited)
+ } == true
}
override fun TypeConstructorMarker.getValueClassProperties(): List<Pair<Name, RigidTypeMarker>>? {