diff --git a/compiler/testData/constraintSystem/severalVariables/recursive/implicitlyRecursive.bounds b/compiler/testData/constraintSystem/severalVariables/recursive/implicitlyRecursive.bounds new file mode 100644 index 00000000000..b3a8ecb359c --- /dev/null +++ b/compiler/testData/constraintSystem/severalVariables/recursive/implicitlyRecursive.bounds @@ -0,0 +1,26 @@ +VARIABLES T P E + +SUBTYPE T Producer

+SUBTYPE P Producer +SUBTYPE E Producer

+ +type parameter bounds: +T <: Producer

*, <: Producer>*, <: Producer>>* +P <: Producer* +E <: Producer

* + +status: +-hasCannotCaptureTypesError: false +-hasConflictingConstraints: false +-hasContradiction: false +-hasErrorInConstrainingTypes: false +-hasTypeConstructorMismatch: false +-hasTypeInferenceIncorporationError: false +-hasUnknownParameters: true +-hasViolatedUpperBound: false +-isSuccessful: false + +result: +T=??? +P=??? +E=??? diff --git a/compiler/testData/constraintSystem/severalVariables/recursive/implicitlyRecursive.constraints b/compiler/testData/constraintSystem/severalVariables/recursive/implicitlyRecursive.constraints new file mode 100644 index 00000000000..da4f2840fc4 --- /dev/null +++ b/compiler/testData/constraintSystem/severalVariables/recursive/implicitlyRecursive.constraints @@ -0,0 +1,5 @@ +VARIABLES T P E + +SUBTYPE T Producer

+SUBTYPE P Producer +SUBTYPE E Producer

diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java index 8186137e300..1557eceb704 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java @@ -441,6 +441,12 @@ public class ConstraintSystemTestGenerated extends AbstractConstraintSystemTest 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") public void testMutuallyRecursive() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/constraintSystem/severalVariables/recursive/mutuallyRecursive.constraints"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt index bb55bb1093a..d424a67d23b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/ConstraintSystemImpl.kt @@ -346,9 +346,10 @@ public class ConstraintSystemImpl : ConstraintSystem { typeVariable: TypeParameterDescriptor, constrainingType: JetType, kind: TypeBounds.BoundKind, - position: ConstraintPosition + position: ConstraintPosition, + derivedFrom: Set = emptySet() ) { - val bound = Bound(typeVariable, constrainingType, kind, position, constrainingType.isProper()) + val bound = Bound(typeVariable, constrainingType, kind, position, constrainingType.isProper(), derivedFrom) val typeBounds = getTypeBounds(typeVariable) if (typeBounds.bounds.contains(bound)) return diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt index bee0e0ab739..78d7963dd8f 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBounds.kt @@ -49,7 +49,9 @@ public trait TypeBounds { public val constrainingType: JetType, public val kind: BoundKind, 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 ) { override fun equals(other: Any?): Boolean { if (this === other) return true diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt index cd640e14e55..cc84ca284fb 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/TypeBoundsImpl.kt @@ -194,7 +194,8 @@ fun Collection.substitute(substituteTypeVariable: (TypeParameterDescripto it.constrainingType } 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() } diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt index e39eab674cd..8066ab7ea18 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/calls/inference/constraintIncorporation.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.Variance.INVARIANT import org.jetbrains.kotlin.types.typeUtil.getNestedTypeArguments import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes +import java.util.* fun ConstraintSystemImpl.incorporateBound(newBound: Bound) { val typeVariable = newBound.typeVariable @@ -92,9 +93,14 @@ private fun ConstraintSystemImpl.generateNewBound(bound: Bound, substitution: Bo fun addNewBound(newConstrainingType: JetType, newBoundKind: BoundKind) { // We don't generate new recursive constraints 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) {