Don't generate constraint if a type variable was substituted twice

to prevent infinite recursion
This commit is contained in:
Svetlana Isakova
2015-06-30 22:05:21 +03:00
parent 3b85ac90ba
commit 89e16ecbcc
7 changed files with 53 additions and 6 deletions
@@ -0,0 +1,26 @@
VARIABLES T P E
SUBTYPE T Producer<P>
SUBTYPE P Producer<E>
SUBTYPE E Producer<P>
type parameter bounds:
T <: Producer<P>*, <: Producer<Producer<E>>*, <: Producer<Producer<Producer<P>>>*
P <: Producer<E>*
E <: Producer<P>*
status:
-hasCannotCaptureTypesError: false
-hasConflictingConstraints: false
-hasContradiction: false
-hasErrorInConstrainingTypes: false
-hasTypeConstructorMismatch: false
-hasTypeInferenceIncorporationError: false
-hasUnknownParameters: true
-hasViolatedUpperBound: false
-isSuccessful: false
result:
T=???
P=???
E=???
@@ -0,0 +1,5 @@
VARIABLES T P E
SUBTYPE T Producer<P>
SUBTYPE P Producer<E>
SUBTYPE E Producer<P>
@@ -441,6 +441,12 @@ public class ConstraintSystemTestGenerated extends AbstractConstraintSystemTest
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/constraintSystem/severalVariables/recursive"), Pattern.compile("^(.+)\\.constraints$"), true); JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/constraintSystem/severalVariables/recursive"), Pattern.compile("^(.+)\\.constraints$"), true);
} }
@TestMetadata("implicitlyRecursive.constraints")
public void testImplicitlyRecursive() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/constraintSystem/severalVariables/recursive/implicitlyRecursive.constraints");
doTest(fileName);
}
@TestMetadata("mutuallyRecursive.constraints") @TestMetadata("mutuallyRecursive.constraints")
public void testMutuallyRecursive() throws Exception { public void testMutuallyRecursive() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/constraintSystem/severalVariables/recursive/mutuallyRecursive.constraints"); String fileName = JetTestUtils.navigationMetadata("compiler/testData/constraintSystem/severalVariables/recursive/mutuallyRecursive.constraints");
@@ -346,9 +346,10 @@ public class ConstraintSystemImpl : ConstraintSystem {
typeVariable: TypeParameterDescriptor, typeVariable: TypeParameterDescriptor,
constrainingType: JetType, constrainingType: JetType,
kind: TypeBounds.BoundKind, kind: TypeBounds.BoundKind,
position: ConstraintPosition position: ConstraintPosition,
derivedFrom: Set<TypeParameterDescriptor> = emptySet()
) { ) {
val bound = Bound(typeVariable, constrainingType, kind, position, constrainingType.isProper()) val bound = Bound(typeVariable, constrainingType, kind, position, constrainingType.isProper(), derivedFrom)
val typeBounds = getTypeBounds(typeVariable) val typeBounds = getTypeBounds(typeVariable)
if (typeBounds.bounds.contains(bound)) return if (typeBounds.bounds.contains(bound)) return
@@ -49,7 +49,9 @@ public trait TypeBounds {
public val constrainingType: JetType, public val constrainingType: JetType,
public val kind: BoundKind, public val kind: BoundKind,
public val position: ConstraintPosition, public val position: ConstraintPosition,
public val isProper: Boolean = true public val isProper: Boolean,
// to prevent infinite recursion in incorporation we store the variables that was substituted to derive this bound
public val derivedFrom: Set<TypeParameterDescriptor>
) { ) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@@ -194,7 +194,8 @@ fun Collection<Bound>.substitute(substituteTypeVariable: (TypeParameterDescripto
it.constrainingType it.constrainingType
} }
substitutedType?.let { type -> substitutedType?.let { type ->
Bound(substituteTypeVariable(it.typeVariable) ?: it.typeVariable, type, it.kind, it.position, it.isProper) Bound(substituteTypeVariable(it.typeVariable) ?: it.typeVariable, type, it.kind, it.position, it.isProper,
it.derivedFrom.map { substituteTypeVariable(it) ?: it }.toSet())
} }
}.filterNotNull() }.filterNotNull()
} }
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.Variance.INVARIANT import org.jetbrains.kotlin.types.Variance.INVARIANT
import org.jetbrains.kotlin.types.typeUtil.getNestedTypeArguments import org.jetbrains.kotlin.types.typeUtil.getNestedTypeArguments
import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes
import java.util.*
fun ConstraintSystemImpl.incorporateBound(newBound: Bound) { fun ConstraintSystemImpl.incorporateBound(newBound: Bound) {
val typeVariable = newBound.typeVariable val typeVariable = newBound.typeVariable
@@ -92,9 +93,14 @@ private fun ConstraintSystemImpl.generateNewBound(bound: Bound, substitution: Bo
fun addNewBound(newConstrainingType: JetType, newBoundKind: BoundKind) { fun addNewBound(newConstrainingType: JetType, newBoundKind: BoundKind) {
// We don't generate new recursive constraints // We don't generate new recursive constraints
val nestedTypeVariables = newConstrainingType.getNestedTypeVariables() val nestedTypeVariables = newConstrainingType.getNestedTypeVariables()
if (nestedTypeVariables.contains(bound.typeVariable) || nestedTypeVariables.contains(substitution.typeVariable)) return if (nestedTypeVariables.contains(bound.typeVariable)) return
addBound(bound.typeVariable, newConstrainingType, newBoundKind, position) // We don't generate constraint if a type variable was substituted twice
val derivedFrom = HashSet(bound.derivedFrom + substitution.derivedFrom)
if (derivedFrom.contains(substitution.typeVariable)) return
derivedFrom.add(substitution.typeVariable)
addBound(bound.typeVariable, newConstrainingType, newBoundKind, position, derivedFrom)
} }
if (substitution.kind == EXACT_BOUND) { if (substitution.kind == EXACT_BOUND) {