[NI] Introduce type depth in CS calculator to avoid SOE

This commit is contained in:
Mikhail Zarechenskiy
2017-07-06 14:49:35 +03:00
parent cf75afba66
commit e7cd615450
6 changed files with 67 additions and 12 deletions
@@ -25,6 +25,11 @@ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
object NewCommonSuperTypeCalculator {
fun commonSuperType(types: List<UnwrappedType>): UnwrappedType {
val maxDepth = types.maxBy { it.typeDepth() }?.typeDepth() ?: 0
return commonSuperType(types, -maxDepth)
}
private fun commonSuperType(types: List<UnwrappedType>, depth: Int): UnwrappedType {
if (types.isEmpty()) throw IllegalStateException("Empty collection for input")
types.singleOrNull()?.let { return it }
@@ -44,19 +49,19 @@ object NewCommonSuperTypeCalculator {
}
}
val lowerSuperType = commonSuperTypeForSimpleTypes(lowers)
val lowerSuperType = commonSuperTypeForSimpleTypes(lowers, depth)
if (!thereIsFlexibleTypes) return lowerSuperType
val upperSuperType = commonSuperTypeForSimpleTypes(types.map { it.upperIfFlexible() })
val upperSuperType = commonSuperTypeForSimpleTypes(types.map { it.upperIfFlexible() }, depth)
return FlexibleTypeImpl(lowerSuperType, upperSuperType)
}
private fun commonSuperTypeForSimpleTypes(types: List<SimpleType>): SimpleType {
private fun commonSuperTypeForSimpleTypes(types: List<SimpleType>, depth: Int): SimpleType {
// i.e. result type also should be marked nullable
val notAllNotNull = types.any { !NullabilityChecker.isSubtypeOfAny(it) }
val notNullTypes = if (notAllNotNull) types.map { it.makeNullableAsSpecified(false) } else types
val commonSuperTypes = commonSuperTypeForNotNullTypes(notNullTypes)
val commonSuperTypes = commonSuperTypeForNotNullTypes(notNullTypes, depth)
return if (notAllNotNull) commonSuperTypes.makeNullableAsSpecified(true) else commonSuperTypes
}
@@ -71,7 +76,7 @@ object NewCommonSuperTypeCalculator {
return result
}
private fun commonSuperTypeForNotNullTypes(types: List<SimpleType>): SimpleType {
private fun commonSuperTypeForNotNullTypes(types: List<SimpleType>, depth: Int): SimpleType {
val uniqueTypes = types.uniquify()
val filteredType = uniqueTypes.filterNot { type ->
uniqueTypes.any { other -> type != other && NewKotlinTypeChecker.isSubtypeOf(type, other)}
@@ -81,11 +86,11 @@ object NewCommonSuperTypeCalculator {
filteredType.singleOrNull()?.let { return it }
return findSuperTypeConstructorsAndIntersectResult(filteredType)
return findSuperTypeConstructorsAndIntersectResult(filteredType, depth)
}
private fun findSuperTypeConstructorsAndIntersectResult(types: List<SimpleType>): SimpleType {
return intersectTypes(allCommonSuperTypeConstructors(types).map { superTypeWithGivenConstructor(types, it) })
private fun findSuperTypeConstructorsAndIntersectResult(types: List<SimpleType>, depth: Int): SimpleType {
return intersectTypes(allCommonSuperTypeConstructors(types).map { superTypeWithGivenConstructor(types, it, depth) })
}
/**
@@ -109,7 +114,11 @@ object NewCommonSuperTypeCalculator {
type.anySuperTypeConstructor { add(it); false }
}
private fun superTypeWithGivenConstructor(types: List<SimpleType>, constructor: TypeConstructor): SimpleType {
private fun superTypeWithGivenConstructor(
types: List<SimpleType>,
constructor: TypeConstructor,
depth: Int
): SimpleType {
if (constructor.parameters.isEmpty()) return KotlinTypeFactory.simpleType(Annotations.EMPTY, constructor, emptyList(), nullable = false)
val typeCheckerContext = TypeCheckerContext(false)
@@ -143,7 +152,7 @@ object NewCommonSuperTypeCalculator {
StarProjectionImpl(parameter)
}
else {
calculateArgument(parameter, typeProjections)
calculateArgument(parameter, typeProjections, depth)
}
arguments.add(argument)
@@ -152,7 +161,11 @@ object NewCommonSuperTypeCalculator {
}
// no star projections in arguments
private fun calculateArgument(parameter: TypeParameterDescriptor, arguments: List<TypeProjection>): TypeProjection {
private fun calculateArgument(parameter: TypeParameterDescriptor, arguments: List<TypeProjection>, depth: Int): TypeProjection {
if (depth > 3) {
return StarProjectionImpl(parameter)
}
// Inv<A>, Inv<A> = Inv<A>
if (parameter.variance == Variance.INVARIANT && arguments.all { it.projectionKind == Variance.INVARIANT }) {
val first = arguments.first()
@@ -183,7 +196,7 @@ object NewCommonSuperTypeCalculator {
// CS(Out<X>, Out<Y>) = Out<CS(X, Y)>
// CS(In<X>, In<Y>) = In<X & Y>
if (asOut) {
val type = commonSuperType(arguments.map { it.type.unwrap() })
val type = commonSuperType(arguments.map { it.type.unwrap() }, depth + 1)
return if (parameter.variance != Variance.INVARIANT) return type.asTypeProjection() else TypeProjectionImpl(Variance.OUT_VARIANCE, type)
}
else {
@@ -0,0 +1,18 @@
class A : FirstOwner<Holder<A>>
class B : SecondOwner<Holder<B>>
interface FirstOwner<T : StubElement<*>> : SecondOwner<T>
interface SecondOwner<T : StubElement<*>>
interface StubElement<T>
interface Holder<T> : StubElement<T>
fun test(a: A?, b: B) {
val c = a ?: b
}
fun box(): String {
test(A(), B())
return "OK"
}
@@ -16952,6 +16952,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("supertypeDepth.kt")
public void testSupertypeDepth() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt");
doTest(fileName);
}
@TestMetadata("typeCastException.kt")
public void testTypeCastException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/typeCastException.kt");
@@ -16952,6 +16952,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("supertypeDepth.kt")
public void testSupertypeDepth() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt");
doTest(fileName);
}
@TestMetadata("typeCastException.kt")
public void testTypeCastException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/typeCastException.kt");
@@ -16952,6 +16952,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("supertypeDepth.kt")
public void testSupertypeDepth() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt");
doTest(fileName);
}
@TestMetadata("typeCastException.kt")
public void testTypeCastException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/typeCastException.kt");
@@ -20732,6 +20732,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
}
@TestMetadata("supertypeDepth.kt")
public void testSupertypeDepth() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/supertypeDepth.kt");
doTest(fileName);
}
@TestMetadata("typeCastException.kt")
public void testTypeCastException() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/regressions/typeCastException.kt");