Recursion depth in CommonSupertypes is bounded by maxDepth + 3

This commit is contained in:
Andrey Breslav
2014-08-27 16:13:47 +04:00
parent 364a7a6574
commit 12d18533e8
8 changed files with 195 additions and 11 deletions
@@ -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
}
+5 -1
View File
@@ -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));
}
}