From e54b25a35ae6ff7cd75c24bd92ccb2f9c31fc644 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 18 Mar 2016 11:28:45 +0300 Subject: [PATCH] Fix StackOverflowError while mapping recursive intersection-type #KT-10972 Fixed --- .../codegen/state/KotlinTypeMapper.java | 10 ++++++++ .../box/regressions/nestedIntersection.kt | 16 ++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 +++++ .../java/lazy/types/LazyJavaTypeResolver.kt | 25 +------------------ .../org/jetbrains/kotlin/types/TypeUtils.kt | 25 +++++++++++++++++++ 5 files changed, 58 insertions(+), 24 deletions(-) create mode 100644 compiler/testData/codegen/box/regressions/nestedIntersection.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index c0a0744eff7..3e0b4060f53 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -76,6 +76,7 @@ import org.jetbrains.kotlin.serialization.deserialization.DeserializedType; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor; import org.jetbrains.kotlin.types.*; +import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import org.jetbrains.kotlin.util.OperatorNameConventions; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.commons.Method; @@ -429,6 +430,15 @@ public class KotlinTypeMapper { TypeConstructor constructor = jetType.getConstructor(); if (constructor instanceof IntersectionTypeConstructor) { jetType = CommonSupertypes.commonSupertype(new ArrayList(constructor.getSupertypes())); + + // interface In + // open class A : In + // open class B : In + // commonSupertype(A, B) = In + // So replace arguments with star-projections to prevent infinite recursive mapping + // It's not very important because such types anyway are prohibited in declarations + jetType = TypeUtilsKt.replaceArgumentsWithStarProjections(jetType); + constructor = jetType.getConstructor(); } DeclarationDescriptor descriptor = constructor.getDeclarationDescriptor(); diff --git a/compiler/testData/codegen/box/regressions/nestedIntersection.kt b/compiler/testData/codegen/box/regressions/nestedIntersection.kt new file mode 100644 index 00000000000..e9e579f5fa9 --- /dev/null +++ b/compiler/testData/codegen/box/regressions/nestedIntersection.kt @@ -0,0 +1,16 @@ +// WITH_RUNTIME + +interface In +open class A : In +open class B : In + +inline fun select(x: T, y: T) = T::class.java.simpleName + +// This test checks mostly that no StackOverflow happens while mapping type argument of select-call (In) +// See KT-10972 +fun foo(): String = select(A(), B()) + +fun box(): String { + if (foo() != "In") return "fail" + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index f6e80f0581f..773cc2532f3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -12193,6 +12193,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("nestedIntersection.kt") + public void testNestedIntersection() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/nestedIntersection.kt"); + doTest(fileName); + } + @TestMetadata("objectCaptureOuterConstructorProperty.kt") public void testObjectCaptureOuterConstructorProperty() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/objectCaptureOuterConstructorProperty.kt"); diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt index ce7d8ab8401..67505655457 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/types/LazyJavaTypeResolver.kt @@ -37,6 +37,7 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.Variance.* import org.jetbrains.kotlin.types.typeUtil.createProjection import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations +import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections import org.jetbrains.kotlin.utils.sure import org.jetbrains.kotlin.utils.toReadOnlyList @@ -415,27 +416,3 @@ internal fun TypeParameterDescriptor.getErasedUpperBound( return defaultValue() } -private fun KotlinType.replaceArgumentsWithStarProjections(): KotlinType { - if (constructor.parameters.isEmpty() || constructor.declarationDescriptor == null) return this - - // We could just create JetTypeImpl with current type constructor and star projections, - // but we want to preserve flexibility of type, and that it what TypeSubstitutor does - return TypeSubstitutor.create(ConstantStarSubstitution).substitute(this, Variance.INVARIANT)!! -} - -private object ConstantStarSubstitution : TypeSubstitution() { - override fun get(key: KotlinType): TypeProjection? { - // Let substitutor deal with flexibility - if (key.isFlexible()) return null - - val newProjections = key.constructor.parameters.map(::StarProjectionImpl) - - val substitution = TypeConstructorSubstitution.create(key.constructor, newProjections) - - return TypeProjectionImpl( - TypeSubstitutor.create(substitution).substitute(key.constructor.declarationDescriptor!!.defaultType, Variance.INVARIANT)!! - ) - } - - override fun isEmpty() = false -} diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index ac31397cc90..e70e9b55eef 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -154,3 +154,28 @@ fun KotlinType.getImmediateSuperclassNotAny(): KotlinType? { fun KotlinType.asTypeProjection(): TypeProjection = TypeProjectionImpl(this) fun KotlinType.contains(predicate: (KotlinType) -> Boolean) = TypeUtils.contains(this, predicate) + +fun KotlinType.replaceArgumentsWithStarProjections(): KotlinType { + if (constructor.parameters.isEmpty() || constructor.declarationDescriptor == null) return this + + // We could just create JetTypeImpl with current type constructor and star projections, + // but we want to preserve flexibility of type, and that it what TypeSubstitutor does + return TypeSubstitutor.create(ConstantStarSubstitution).substitute(this, Variance.INVARIANT)!! +} + +private object ConstantStarSubstitution : TypeSubstitution() { + override fun get(key: KotlinType): TypeProjection? { + // Let substitutor deal with flexibility + if (key.isFlexible()) return null + + val newProjections = key.constructor.parameters.map(::StarProjectionImpl) + + val substitution = TypeConstructorSubstitution.create(key.constructor, newProjections) + + return TypeProjectionImpl( + TypeSubstitutor.create(substitution).substitute(key.constructor.declarationDescriptor!!.defaultType, Variance.INVARIANT)!! + ) + } + + override fun isEmpty() = false +}