Recursion depth in CommonSupertypes is bounded by maxDepth + 3
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
// KT-5943 StackOverflowError from commonSupertype of two enums
|
||||
|
||||
enum class A { A }
|
||||
enum class B { B }
|
||||
|
||||
val x = if (true) A.A else B.B
|
||||
@@ -0,0 +1,65 @@
|
||||
package
|
||||
|
||||
internal val x: kotlin.Enum<out kotlin.Enum<out kotlin.Enum<out kotlin.Enum<out kotlin.Enum<out kotlin.Any?>>>>>
|
||||
|
||||
internal final enum class A : kotlin.Enum<A> {
|
||||
private constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public enum entry A : A {
|
||||
private constructor A()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public class object <class-object-for-A> : A.A {
|
||||
private constructor <class-object-for-A>()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
// Static members
|
||||
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): A
|
||||
public final /*synthesized*/ fun values(): kotlin.Array<A>
|
||||
}
|
||||
|
||||
internal final enum class B : kotlin.Enum<B> {
|
||||
private constructor B()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public enum entry B : B {
|
||||
private constructor B()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
|
||||
public class object <class-object-for-B> : B.B {
|
||||
private constructor <class-object-for-B>()
|
||||
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
|
||||
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
|
||||
public final override /*1*/ /*fake_override*/ fun name(): kotlin.String
|
||||
public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int
|
||||
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
|
||||
}
|
||||
}
|
||||
|
||||
// Static members
|
||||
public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): B
|
||||
public final /*synthesized*/ fun values(): kotlin.Array<B>
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// !DIAGNOSTICS: -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE
|
||||
|
||||
trait Rec<T>
|
||||
class A : Rec<A>
|
||||
class B : Rec<B>
|
||||
|
||||
fun test(a: A, b: B, c: Boolean) {
|
||||
var ab = if (c) a else b
|
||||
ab = a
|
||||
ab = b
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// !DIAGNOSTICS: -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE
|
||||
|
||||
// FILE: p/Rec.java
|
||||
|
||||
package p;
|
||||
|
||||
public interface Rec<T> {
|
||||
|
||||
}
|
||||
|
||||
// FILE: p/A.java
|
||||
|
||||
package p;
|
||||
|
||||
public interface A extends Rec<A> {
|
||||
|
||||
}
|
||||
|
||||
// FILE: p/B.java
|
||||
|
||||
package p;
|
||||
|
||||
public interface B extends Rec<B> {
|
||||
|
||||
}
|
||||
|
||||
// FILE: k.kt
|
||||
|
||||
import p.*
|
||||
|
||||
fun test(a: A, b: B, c: Boolean) {
|
||||
var ab = if (c) a else b
|
||||
ab = a
|
||||
ab = b
|
||||
}
|
||||
@@ -40,4 +40,8 @@ fun f<T>(a : Float) : T {a}
|
||||
|
||||
trait Parent
|
||||
trait A: Parent
|
||||
trait B: Parent
|
||||
trait B: Parent
|
||||
|
||||
trait Rec<T>
|
||||
class ARec : Rec<ARec>
|
||||
class BRec : Rec<BRec>
|
||||
@@ -3763,6 +3763,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("commonSupertype.kt")
|
||||
public void testCommonSupertype() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/commonSupertype.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dontCreatePackageTypeForEnumEntry.kt")
|
||||
public void testDontCreatePackageTypeForEnumEntry() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/enum/dontCreatePackageTypeForEnumEntry.kt");
|
||||
@@ -4437,6 +4443,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("recursive.kt")
|
||||
public void testRecursive() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/generics/recursive.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("RecursiveUpperBoundCheck.kt")
|
||||
public void testRecursiveUpperBoundCheck() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/generics/RecursiveUpperBoundCheck.kt");
|
||||
@@ -7763,6 +7774,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("recursiveGeneric.kt")
|
||||
public void testRecursiveGeneric() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/recursiveGeneric.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("stringOrNull.kt")
|
||||
public void testStringOrNull() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/stringOrNull.kt");
|
||||
|
||||
@@ -204,6 +204,10 @@ public class JetTypeCheckerTest extends JetLiteFixture {
|
||||
assertCommonSupertype("Base_T<out Parent>", "Base_T<A>", "Base_T<B>");
|
||||
}
|
||||
|
||||
public void testCommonSupertypesForRecursive() throws Exception {
|
||||
assertCommonSupertype("Rec<out Rec<out Rec<out Rec<out Rec<out Any?>>>>>", "ARec", "BRec");
|
||||
}
|
||||
|
||||
public void testIntersect() throws Exception {
|
||||
assertIntersection("Long", "Long?", "Number");
|
||||
assertIntersection("Long", "Number", "Long?");
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.jet.lang.types;
|
||||
|
||||
import kotlin.Function1;
|
||||
import kotlin.KotlinPackage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
|
||||
@@ -47,6 +49,34 @@ public class CommonSupertypes {
|
||||
|
||||
@NotNull
|
||||
public static JetType commonSupertype(@NotNull Collection<JetType> types) {
|
||||
// Recursion should not be significantly deeper than the deepest type in question
|
||||
// It can be slightly deeper, though: e.g. when initial types are simple, but their supertypes are complex
|
||||
return findCommonSupertype(types, 0, maxDepth(types) + 3);
|
||||
}
|
||||
|
||||
private static int maxDepth(@NotNull Collection<JetType> types) {
|
||||
int max = 0;
|
||||
for (JetType type : types) {
|
||||
int depth = depth(type);
|
||||
if (max < depth) {
|
||||
max = depth;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
private static int depth(@NotNull JetType type) {
|
||||
return 1 + maxDepth(KotlinPackage.map(type.getArguments(), new Function1<TypeProjection, JetType>() {
|
||||
@Override
|
||||
public JetType invoke(TypeProjection projection) {
|
||||
return projection.getType();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JetType findCommonSupertype(@NotNull Collection<JetType> types, int recursionDepth, int maxDepth) {
|
||||
assert recursionDepth <= maxDepth : "Recursion depth exceeded: " + recursionDepth + " > " + maxDepth + " for types " + types;
|
||||
boolean hasFlexible = false;
|
||||
List<JetType> upper = new ArrayList<JetType>(types.size());
|
||||
List<JetType> lower = new ArrayList<JetType>(types.size());
|
||||
@@ -63,12 +93,15 @@ public class CommonSupertypes {
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasFlexible) return commonSuperTypeForInflexible(types);
|
||||
return DelegatingFlexibleType.OBJECT$.create(commonSuperTypeForInflexible(lower), commonSuperTypeForInflexible(upper));
|
||||
if (!hasFlexible) return commonSuperTypeForInflexible(types, recursionDepth, maxDepth);
|
||||
return DelegatingFlexibleType.OBJECT$.create(
|
||||
commonSuperTypeForInflexible(lower, recursionDepth, maxDepth),
|
||||
commonSuperTypeForInflexible(upper, recursionDepth, maxDepth)
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JetType commonSuperTypeForInflexible(@NotNull Collection<JetType> types) {
|
||||
private static JetType commonSuperTypeForInflexible(@NotNull Collection<JetType> types, int recursionDepth, int maxDepth) {
|
||||
assert !types.isEmpty();
|
||||
Collection<JetType> typeSet = new HashSet<JetType>(types);
|
||||
if (typeSet.size() == 1) return typeSet.iterator().next();
|
||||
@@ -114,7 +147,7 @@ public class CommonSupertypes {
|
||||
Map.Entry<TypeConstructor, Set<JetType>> entry = commonSupertypes.entrySet().iterator().next();
|
||||
|
||||
// Reconstructing type arguments if possible
|
||||
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue());
|
||||
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue(), recursionDepth, maxDepth);
|
||||
return TypeUtils.makeNullableIfNeeded(result, nullable);
|
||||
}
|
||||
|
||||
@@ -160,7 +193,7 @@ public class CommonSupertypes {
|
||||
// constructor - type constructor of a supertype to be instantiated
|
||||
// types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
|
||||
@NotNull
|
||||
private static JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types) {
|
||||
private static JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types, int recursionDepth, int maxDepth) {
|
||||
// we assume that all the given types are applications of the same type constructor
|
||||
|
||||
assert !types.isEmpty();
|
||||
@@ -177,7 +210,7 @@ public class CommonSupertypes {
|
||||
for (JetType type : types) {
|
||||
typeProjections.add(type.getArguments().get(i));
|
||||
}
|
||||
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections));
|
||||
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections, recursionDepth, maxDepth));
|
||||
}
|
||||
|
||||
boolean nullable = false;
|
||||
@@ -195,11 +228,21 @@ public class CommonSupertypes {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set<TypeProjection> typeProjections) {
|
||||
private static TypeProjection computeSupertypeProjection(
|
||||
@NotNull TypeParameterDescriptor parameterDescriptor,
|
||||
@NotNull Set<TypeProjection> typeProjections,
|
||||
int recursionDepth, int maxDepth
|
||||
) {
|
||||
if (typeProjections.size() == 1) {
|
||||
return typeProjections.iterator().next();
|
||||
}
|
||||
|
||||
if (recursionDepth >= maxDepth) {
|
||||
// If recursion is too deep, we cut it by taking <out Any?> as an ultimate supertype argument
|
||||
// Example: class A : Base<A>; class B : Base<B>, commonSuperType(A, B) = Base<out Any?>
|
||||
return new TypeProjectionImpl(OUT_VARIANCE, KotlinBuiltIns.getInstance().getNullableAnyType());
|
||||
}
|
||||
|
||||
Set<JetType> ins = new HashSet<JetType>();
|
||||
Set<JetType> outs = new HashSet<JetType>();
|
||||
|
||||
@@ -239,19 +282,19 @@ public class CommonSupertypes {
|
||||
|
||||
if (outs != null) {
|
||||
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
|
||||
return new TypeProjectionImpl(projectionKind, commonSupertype(outs));
|
||||
return new TypeProjectionImpl(projectionKind, findCommonSupertype(outs, recursionDepth + 1, maxDepth));
|
||||
}
|
||||
if (ins != null) {
|
||||
JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, ins);
|
||||
if (intersection == null) {
|
||||
return new TypeProjectionImpl(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds()));
|
||||
return new TypeProjectionImpl(OUT_VARIANCE, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth));
|
||||
}
|
||||
Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
|
||||
return new TypeProjectionImpl(projectionKind, intersection);
|
||||
}
|
||||
else {
|
||||
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
|
||||
return new TypeProjectionImpl(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
|
||||
return new TypeProjectionImpl(projectionKind, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user