diff --git a/compiler/testData/constraintSystem/declarations.kt b/compiler/testData/constraintSystem/declarations.kt index 7c8614eadfa..6c16f640220 100644 --- a/compiler/testData/constraintSystem/declarations.kt +++ b/compiler/testData/constraintSystem/declarations.kt @@ -6,6 +6,7 @@ interface C : B interface Consumer interface Producer +interface Inv interface My interface Successor : My diff --git a/compiler/testData/constraintSystem/severalVariables/recursive/kt8879.bounds b/compiler/testData/constraintSystem/severalVariables/recursive/kt8879.bounds new file mode 100644 index 00000000000..024cedb4281 --- /dev/null +++ b/compiler/testData/constraintSystem/severalVariables/recursive/kt8879.bounds @@ -0,0 +1,23 @@ +VARIABLES T P + +T <: My +Inv

<: Inv + +type parameter bounds: +T <: My*, := P*, <: My>*, <: My

*, <: My>*, <: My>>* +P := T*, <: My*, <: My>*, <: My

*, <: My>*, <: My>>* + +status: +-hasCannotCaptureTypesError: false +-hasConflictingConstraints: false +-hasContradiction: false +-hasErrorInConstrainingTypes: false +-hasParameterConstraintError: false +-hasTypeInferenceIncorporationError: false +-hasUnknownParameters: true +-hasViolatedUpperBound: false +-isSuccessful: false + +result: +T=??? +P=??? diff --git a/compiler/testData/constraintSystem/severalVariables/recursive/kt8879.constraints b/compiler/testData/constraintSystem/severalVariables/recursive/kt8879.constraints new file mode 100644 index 00000000000..9ab335fa05c --- /dev/null +++ b/compiler/testData/constraintSystem/severalVariables/recursive/kt8879.constraints @@ -0,0 +1,4 @@ +VARIABLES T P + +T <: My +Inv

<: Inv diff --git a/compiler/testData/diagnostics/tests/inference/constraints/kt8879.kt b/compiler/testData/diagnostics/tests/inference/constraints/kt8879.kt new file mode 100644 index 00000000000..efc56fafdd7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/constraints/kt8879.kt @@ -0,0 +1,12 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER + +interface Inv +interface Inv2 + +fun > foo(klass: Inv): String? = null + +fun bar(): Inv = null!! + +fun test() { + foo(bar()) +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/constraints/kt8879.txt b/compiler/testData/diagnostics/tests/inference/constraints/kt8879.txt new file mode 100644 index 00000000000..f48907af1ee --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/constraints/kt8879.txt @@ -0,0 +1,17 @@ +package + +internal fun bar(): Inv +internal fun > foo(/*0*/ klass: Inv): kotlin.String? +internal fun test(): kotlin.Unit + +internal interface Inv { + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +internal interface Inv2 { + 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 open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index e8384820870..0f407175ef2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -7226,6 +7226,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("kt8879.kt") + public void testKt8879() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/constraints/kt8879.kt"); + doTest(fileName); + } + @TestMetadata("notNullConstraintOnNullableType.kt") public void testNotNullConstraintOnNullableType() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/constraints/notNullConstraintOnNullableType.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt index d1031c6d7fd..976de794630 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/AbstractConstraintSystemTest.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.diagnostics.rendering.Renderers import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.TypeResolver +import org.jetbrains.kotlin.resolve.calls.inference.ConstraintContext import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPositionKind.SPECIAL import org.jetbrains.kotlin.resolve.calls.inference.registerTypeVariables @@ -92,12 +93,12 @@ abstract public class AbstractConstraintSystemTest() : JetLiteFixture() { for (constraint in constraints) { val firstType = testDeclarations.getType(constraint.firstType).assertNotError() val secondType = testDeclarations.getType(constraint.secondType).assertNotError() - val position = SPECIAL.position() + val context = ConstraintContext(SPECIAL.position()) when (constraint.kind) { - MyConstraintKind.SUBTYPE -> constraintSystem.addSubtypeConstraint(firstType, secondType, position) - MyConstraintKind.SUPERTYPE -> constraintSystem.addSupertypeConstraint(firstType, secondType, position) + MyConstraintKind.SUBTYPE -> constraintSystem.addSubtypeConstraint(firstType, secondType, context.position) + MyConstraintKind.SUPERTYPE -> constraintSystem.addSupertypeConstraint(firstType, secondType, context.position) MyConstraintKind.EQUAL -> constraintSystem.addConstraint( - ConstraintSystemImpl.ConstraintKind.EQUAL, firstType, secondType, position, topLevel = true) + ConstraintSystemImpl.ConstraintKind.EQUAL, firstType, secondType, context, topLevel = true) } } if (fixVariables) constraintSystem.fixVariables() diff --git a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java index 4df6fba0cd3..19e745ee98c 100644 --- a/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/resolve/constraintSystem/ConstraintSystemTestGenerated.java @@ -720,6 +720,12 @@ public class ConstraintSystemTestGenerated extends AbstractConstraintSystemTest doTest(fileName); } + @TestMetadata("kt8879.constraints") + public void testKt8879() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/constraintSystem/severalVariables/recursive/kt8879.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 f598e6c67e8..a528e5b1547 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 @@ -153,8 +153,8 @@ public class ConstraintSystemImpl : ConstraintSystem { for ((typeVariable, typeBounds) in allTypeParameterBounds) { for (declaredUpperBound in typeVariable.getUpperBounds()) { if (declaredUpperBound.isDefaultBound()) continue //todo remove this line (?) - val position = TYPE_BOUND_POSITION.position(typeVariable.getIndex()) - addBound(typeVariable, declaredUpperBound, UPPER_BOUND, position) + val context = ConstraintContext(TYPE_BOUND_POSITION.position(typeVariable.getIndex())) + addBound(typeVariable, declaredUpperBound, UPPER_BOUND, context) } } } @@ -204,27 +204,31 @@ public class ConstraintSystemImpl : ConstraintSystem { if (constrainingType != null && TypeUtils.noExpectedType(constrainingType)) return val newSubjectType = originalToVariablesSubstitutor.substitute(subjectType, Variance.INVARIANT) - addConstraint(SUB_TYPE, newSubjectType, constrainingType, constraintPosition, topLevel = true) + addConstraint(SUB_TYPE, newSubjectType, constrainingType, ConstraintContext(constraintPosition), topLevel = true) } override fun addSubtypeConstraint(constrainingType: JetType?, subjectType: JetType, constraintPosition: ConstraintPosition) { val newSubjectType = originalToVariablesSubstitutor.substitute(subjectType, Variance.INVARIANT) - addConstraint(SUB_TYPE, constrainingType, newSubjectType, constraintPosition, topLevel = true) + addConstraint(SUB_TYPE, constrainingType, newSubjectType, ConstraintContext(constraintPosition), topLevel = true) } fun addConstraint( constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, - constraintPosition: ConstraintPosition, + constraintContext: ConstraintContext, topLevel: Boolean ) { + val constraintPosition = constraintContext.position + + // when processing nested constraints, `derivedFrom` information should be reset + val newConstraintContext = ConstraintContext(constraintContext.position, derivedFrom = null) val typeCheckingProcedure = TypeCheckingProcedure(object : TypeCheckingProcedureCallbacks { private var depth = 0 override fun assertEqualTypes(a: JetType, b: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean { depth++ - doAddConstraint(EQUAL, a, b, constraintPosition, typeCheckingProcedure, topLevel = false) + doAddConstraint(EQUAL, a, b, newConstraintContext, typeCheckingProcedure, topLevel = false) depth-- return true @@ -236,7 +240,7 @@ public class ConstraintSystemImpl : ConstraintSystem { override fun assertSubtype(subtype: JetType, supertype: JetType, typeCheckingProcedure: TypeCheckingProcedure): Boolean { depth++ - doAddConstraint(SUB_TYPE, subtype, supertype, constraintPosition, typeCheckingProcedure, topLevel = false) + doAddConstraint(SUB_TYPE, subtype, supertype, newConstraintContext, typeCheckingProcedure, topLevel = false) depth-- return true } @@ -249,7 +253,7 @@ public class ConstraintSystemImpl : ConstraintSystem { if (depth > 0) { errors.add(CannotCapture(constraintPosition, myTypeVariable)) } - generateTypeParameterCaptureConstraint(typeVariable, typeProjection, constraintPosition) + generateTypeParameterCaptureConstraint(typeVariable, typeProjection, newConstraintContext) return true } return false @@ -260,7 +264,7 @@ public class ConstraintSystemImpl : ConstraintSystem { return true } }) - doAddConstraint(constraintKind, subType, superType, constraintPosition, typeCheckingProcedure, topLevel) + doAddConstraint(constraintKind, subType, superType, constraintContext, typeCheckingProcedure, topLevel) } private fun isErrorOrSpecialType(type: JetType?, constraintPosition: ConstraintPosition): Boolean { @@ -279,10 +283,11 @@ public class ConstraintSystemImpl : ConstraintSystem { constraintKind: ConstraintKind, subType: JetType?, superType: JetType?, - constraintPosition: ConstraintPosition, + constraintContext: ConstraintContext, typeCheckingProcedure: TypeCheckingProcedure, topLevel: Boolean ) { + val constraintPosition = constraintContext.position if (isErrorOrSpecialType(subType, constraintPosition) || isErrorOrSpecialType(superType, constraintPosition)) return if (subType == null || superType == null) return @@ -306,11 +311,11 @@ public class ConstraintSystemImpl : ConstraintSystem { fun simplifyConstraint(subType: JetType, superType: JetType) { if (isMyTypeVariable(subType)) { - generateTypeParameterBound(subType, superType, constraintKind.toBound(), constraintPosition) + generateTypeParameterBound(subType, superType, constraintKind.toBound(), constraintContext) return } if (isMyTypeVariable(superType)) { - generateTypeParameterBound(superType, subType, constraintKind.toBound().reverse(), constraintPosition) + generateTypeParameterBound(superType, subType, constraintKind.toBound().reverse(), constraintContext) return } // if subType is nullable and superType is not nullable, unsafe call or type mismatch error will be generated later, @@ -335,10 +340,10 @@ public class ConstraintSystemImpl : ConstraintSystem { typeVariable: TypeParameterDescriptor, constrainingType: JetType, kind: TypeBounds.BoundKind, - position: ConstraintPosition, - derivedFrom: Set = emptySet() + constraintContext: ConstraintContext ) { - val bound = Bound(typeVariable, constrainingType, kind, position, constrainingType.isProper(), derivedFrom) + val bound = Bound(typeVariable, constrainingType, kind, constraintContext.position, + constrainingType.isProper(), constraintContext.derivedFrom ?: emptySet()) val typeBounds = getTypeBounds(typeVariable) if (typeBounds.bounds.contains(bound)) return @@ -358,7 +363,7 @@ public class ConstraintSystemImpl : ConstraintSystem { parameterType: JetType, constrainingType: JetType, boundKind: TypeBounds.BoundKind, - constraintPosition: ConstraintPosition + constraintContext: ConstraintContext ) { val typeVariable = getMyTypeVariable(parameterType)!! @@ -380,7 +385,7 @@ public class ConstraintSystemImpl : ConstraintSystem { } if (!parameterType.isMarkedNullable() || !TypeUtils.isNullableType(newConstrainingType)) { - addBound(typeVariable, newConstrainingType, boundKind, constraintPosition) + addBound(typeVariable, newConstrainingType, boundKind, constraintContext) return } // For parameter type T: @@ -390,23 +395,23 @@ public class ConstraintSystemImpl : ConstraintSystem { // constraints T? >: Int?; T? >: Int! should transform to T >: Int val notNullConstrainingType = TypeUtils.makeNotNullable(newConstrainingType) if (boundKind == EXACT_BOUND || boundKind == LOWER_BOUND) { - addBound(typeVariable, notNullConstrainingType, LOWER_BOUND, constraintPosition) + addBound(typeVariable, notNullConstrainingType, LOWER_BOUND, constraintContext) } // constraints T? <: Int?; T? <: Int! should transform to T <: Int?; T <: Int! correspondingly if (boundKind == EXACT_BOUND || boundKind == UPPER_BOUND) { - addBound(typeVariable, newConstrainingType, UPPER_BOUND, constraintPosition) + addBound(typeVariable, newConstrainingType, UPPER_BOUND, constraintContext) } } private fun generateTypeParameterCaptureConstraint( parameterType: JetType, constrainingTypeProjection: TypeProjection, - constraintPosition: ConstraintPosition + constraintContext: ConstraintContext ) { val typeVariable = getMyTypeVariable(parameterType)!! if (!typeVariable.getUpperBoundsAsType().isDefaultBound() && constrainingTypeProjection.getProjectionKind() == Variance.IN_VARIANCE) { - errors.add(CannotCapture(constraintPosition, typeVariable)) + errors.add(CannotCapture(constraintContext.position, typeVariable)) } val typeProjection = if (parameterType.isMarkedNullable()) { TypeProjectionImpl(constrainingTypeProjection.getProjectionKind(), TypeUtils.makeNotNullable(constrainingTypeProjection.getType())) @@ -415,7 +420,7 @@ public class ConstraintSystemImpl : ConstraintSystem { constrainingTypeProjection } val capturedType = createCapturedType(typeProjection) - addBound(typeVariable, capturedType, EXACT_BOUND, constraintPosition) + addBound(typeVariable, capturedType, EXACT_BOUND, constraintContext) } override fun getTypeVariables() = originalToVariables.keySet() @@ -483,7 +488,7 @@ public class ConstraintSystemImpl : ConstraintSystem { val value = typeBounds.value ?: return - addBound(typeVariable, value, TypeBounds.BoundKind.EXACT_BOUND, ConstraintPositionKind.FROM_COMPLETER.position()) + addBound(typeVariable, value, TypeBounds.BoundKind.EXACT_BOUND, ConstraintContext(ConstraintPositionKind.FROM_COMPLETER.position())) } fun fixVariables() { 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 3abccc2ffde..8c992999ecb 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 @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.resolve.calls.inference +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.EQUAL import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemImpl.ConstraintKind.SUB_TYPE import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.Bound @@ -24,12 +25,18 @@ import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.EXACT_B import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.LOWER_BOUND import org.jetbrains.kotlin.resolve.calls.inference.TypeBounds.BoundKind.UPPER_BOUND import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.CompoundConstraintPosition +import org.jetbrains.kotlin.resolve.calls.inference.constraintPosition.ConstraintPosition import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.Variance.INVARIANT import org.jetbrains.kotlin.types.typeUtil.getNestedArguments import org.jetbrains.kotlin.types.typesApproximation.approximateCapturedTypes import java.util.* +data class ConstraintContext( + val position: ConstraintPosition, + // see TypeBounds.Bound.derivedFrom + val derivedFrom: Set? = null) + fun ConstraintSystemImpl.incorporateBound(newBound: Bound) { val typeVariable = newBound.typeVariable val typeBounds = getTypeBounds(typeVariable) @@ -45,7 +52,8 @@ fun ConstraintSystemImpl.incorporateBound(newBound: Bound) { val constrainingType = newBound.constrainingType if (isMyTypeVariable(constrainingType)) { - addBound(getMyTypeVariable(constrainingType)!!, typeVariable.correspondingType, newBound.kind.reverse(), newBound.position) + val context = ConstraintContext(newBound.position, newBound.derivedFrom) + addBound(getMyTypeVariable(constrainingType)!!, typeVariable.correspondingType, newBound.kind.reverse(), context) return } constrainingType.getNestedTypeVariables().forEach { @@ -61,12 +69,12 @@ private fun ConstraintSystemImpl.addConstraintFromBounds(old: Bound, new: Bound) val oldType = old.constrainingType val newType = new.constrainingType - val position = CompoundConstraintPosition(old.position, new.position) + val context = ConstraintContext(CompoundConstraintPosition(old.position, new.position), old.derivedFrom + new.derivedFrom) when { - old.kind.ordinal() < new.kind.ordinal() -> addConstraint(SUB_TYPE, oldType, newType, position, topLevel = false) - old.kind.ordinal() > new.kind.ordinal() -> addConstraint(SUB_TYPE, newType, oldType, position, topLevel = false) - old.kind == new.kind && old.kind == EXACT_BOUND -> addConstraint(EQUAL, oldType, newType, position, topLevel = false) + old.kind.ordinal() < new.kind.ordinal() -> addConstraint(SUB_TYPE, oldType, newType, context, topLevel = false) + old.kind.ordinal() > new.kind.ordinal() -> addConstraint(SUB_TYPE, newType, oldType, context, topLevel = false) + old.kind == new.kind && old.kind == EXACT_BOUND -> addConstraint(EQUAL, oldType, newType, context, topLevel = false) } } @@ -97,7 +105,7 @@ private fun ConstraintSystemImpl.generateNewBound(bound: Bound, substitution: Bo if (derivedFrom.contains(substitution.typeVariable)) return derivedFrom.add(substitution.typeVariable) - addBound(bound.typeVariable, newConstrainingType, newBoundKind, position, derivedFrom) + addBound(bound.typeVariable, newConstrainingType, newBoundKind, ConstraintContext(position, derivedFrom)) } if (substitution.kind == EXACT_BOUND) {