From 86dc0925b105eb36d7fcba23c3970f4c52d61158 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Wed, 11 Dec 2019 16:35:08 +0300 Subject: [PATCH] [NI] Preserve constraint position for OnlyInputType during incorporation Variable with complex dependency on other variable loses its original constraint position when variable it depends on is fixed. To prevent that, original input position is saved in incorporation position. --- .../components/ConstraintIncorporator.kt | 9 +-- .../components/ConstraintInjector.kt | 24 ++++--- .../model/ConstraintPositionAndErrors.kt | 20 ++++-- .../model/MutableConstraintStorage.kt | 20 +++--- .../model/NewConstraintSystemImpl.kt | 8 +-- .../onlyInputTypesCommonConstraintSystem.kt | 54 ++++++++++++++ .../onlyInputTypesCommonConstraintSystem.txt | 72 +++++++++++++++++++ .../DiagnosticsTestWithStdLibGenerated.java | 5 ++ ...ticsTestWithStdLibUsingJavacGenerated.java | 5 ++ 9 files changed, 184 insertions(+), 33 deletions(-) create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.txt diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt index 00c7ba762bd..1fac1dba67f 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt @@ -5,9 +5,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components -import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint -import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind -import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints +import org.jetbrains.kotlin.resolve.calls.inference.model.* import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.utils.SmartSet @@ -174,7 +172,10 @@ class ConstraintIncorporator( val kind = if (isSubtype) ConstraintKind.LOWER else ConstraintKind.UPPER - addNewIncorporatedConstraint(targetVariable, newConstraint, ConstraintContext(kind, derivedFrom)) + val inputTypePosition = if (baseConstraint.position.from is OnlyInputTypeConstraintPosition) + baseConstraint.position else null + + addNewIncorporatedConstraint(targetVariable, newConstraint, ConstraintContext(kind, derivedFrom, inputTypePosition)) } fun Context.containsConstrainingTypeWithoutProjection( diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt index 6e3f465e622..ab284eb30b9 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintInjector.kt @@ -48,21 +48,19 @@ class ConstraintInjector( fun addInitialSubtypeConstraint(c: Context, lowerType: KotlinTypeMarker, upperType: KotlinTypeMarker, position: ConstraintPosition) { val initialConstraint = InitialConstraint(lowerType, upperType, UPPER, position) - val incorporationPosition = IncorporationConstraintPosition(position, initialConstraint) c.addInitialConstraint(initialConstraint) updateAllowedTypeDepth(c, lowerType) updateAllowedTypeDepth(c, upperType) - addSubTypeConstraintAndIncorporateIt(c, lowerType, upperType, incorporationPosition) + addSubTypeConstraintAndIncorporateIt(c, lowerType, upperType, IncorporationConstraintPosition(position, initialConstraint)) } fun addInitialEqualityConstraint(c: Context, a: KotlinTypeMarker, b: KotlinTypeMarker, position: ConstraintPosition) { val initialConstraint = InitialConstraint(a, b, ConstraintKind.EQUALITY, position) - val incorporationPosition = IncorporationConstraintPosition(position, initialConstraint) c.addInitialConstraint(initialConstraint) updateAllowedTypeDepth(c, a) updateAllowedTypeDepth(c, b) - addSubTypeConstraintAndIncorporateIt(c, a, b, incorporationPosition) - addSubTypeConstraintAndIncorporateIt(c, b, a, incorporationPosition) + addSubTypeConstraintAndIncorporateIt(c, a, b, IncorporationConstraintPosition(position, initialConstraint)) + addSubTypeConstraintAndIncorporateIt(c, b, a, IncorporationConstraintPosition(position, initialConstraint)) } private fun addSubTypeConstraintAndIncorporateIt( @@ -72,7 +70,7 @@ class ConstraintInjector( incorporatePosition: IncorporationConstraintPosition ) { val possibleNewConstraints = Stack>() - val typeCheckerContext = TypeCheckerContext(c, incorporatePosition, lowerType, upperType, possibleNewConstraints) + val typeCheckerContext = TypeCheckerContext(c, incorporatePosition, lowerType, upperType, possibleNewConstraints, mutableSetOf()) typeCheckerContext.runIsSubtypeOf(lowerType, upperType) while (possibleNewConstraints.isNotEmpty()) { @@ -87,6 +85,8 @@ class ConstraintInjector( constraintIncorporator.incorporate(typeCheckerContext, typeVariable, it) } } + + incorporatePosition.inputTypePositions = typeCheckerContext.preservedInputTypePositions } private fun updateAllowedTypeDepth(c: Context, initialType: KotlinTypeMarker) = with(c) { @@ -122,7 +122,8 @@ class ConstraintInjector( val position: IncorporationConstraintPosition, val baseLowerType: KotlinTypeMarker, val baseUpperType: KotlinTypeMarker, - val possibleNewConstraints: MutableList> + val possibleNewConstraints: MutableList>, + val preservedInputTypePositions: MutableSet ) : AbstractTypeCheckerContextForConstraintSystem(), ConstraintIncorporator.Context, TypeSystemInferenceExtensionContext by c { val baseContext: AbstractTypeCheckerContext = newBaseTypeCheckerContext(isErrorTypeEqualsToAnything, isStubTypeEqualsToAnything) @@ -193,7 +194,7 @@ class ConstraintInjector( type: KotlinTypeMarker, constraintContext: ConstraintContext ) { - val (kind, derivedFrom) = constraintContext + val (kind, derivedFrom, inputTypePosition) = constraintContext var targetType = type if (targetType.isUninferredParameter()) { @@ -231,6 +232,7 @@ class ConstraintInjector( } possibleNewConstraints.add(typeVariable to Constraint(kind, targetType, position, derivedFrom = derivedFrom)) + inputTypePosition?.let { preservedInputTypePositions.add(it) } } override val allTypeVariablesWithConstraints: Collection @@ -259,4 +261,8 @@ class ConstraintInjector( } } -data class ConstraintContext(val kind: ConstraintKind, val derivedFrom: Set) +data class ConstraintContext( + val kind: ConstraintKind, + val derivedFrom: Set, + val inputTypePosition: ConstraintPosition? = null +) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt index 980d795b627..39b7ed72519 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/ConstraintPositionAndErrors.kt @@ -29,11 +29,11 @@ import org.jetbrains.kotlin.types.model.TypeVariableMarker sealed class ConstraintPosition -class ExplicitTypeParameterConstraintPosition(val typeArgument: SimpleTypeArgument) : ConstraintPosition() { +class ExplicitTypeParameterConstraintPosition(val typeArgument: SimpleTypeArgument) : ConstraintPosition(), OnlyInputTypeConstraintPosition { override fun toString() = "TypeParameter $typeArgument" } -class ExpectedTypeConstraintPosition(val topLevelCall: KotlinCall) : ConstraintPosition() { +class ExpectedTypeConstraintPosition(val topLevelCall: KotlinCall) : ConstraintPosition(), OnlyInputTypeConstraintPosition { override fun toString() = "ExpectedType for call $topLevelCall" } @@ -45,11 +45,13 @@ class DeclaredUpperBoundConstraintPositionImpl(val typeParameterDescriptor: Type class FirDeclaredUpperBoundConstraintPosition : DeclaredUpperBoundConstraintPosition() -class ArgumentConstraintPosition(val argument: KotlinCallArgument) : ConstraintPosition() { +interface OnlyInputTypeConstraintPosition + +class ArgumentConstraintPosition(val argument: KotlinCallArgument) : ConstraintPosition(), OnlyInputTypeConstraintPosition { override fun toString() = "Argument $argument" } -class ReceiverConstraintPosition(val argument: KotlinCallArgument) : ConstraintPosition() { +class ReceiverConstraintPosition(val argument: KotlinCallArgument) : ConstraintPosition(), OnlyInputTypeConstraintPosition { override fun toString() = "Receiver $argument" } @@ -77,8 +79,14 @@ class DelegatedPropertyConstraintPosition(val topLevelCall: KotlinCall) : Constr override fun toString() = "Constraint from call $topLevelCall for delegated property" } -class IncorporationConstraintPosition(val from: ConstraintPosition, val initialConstraint: InitialConstraint) : ConstraintPosition() { - override fun toString() = "Incorporate $initialConstraint from position $from" +class IncorporationConstraintPosition( + val from: ConstraintPosition, + val initialConstraint: InitialConstraint +) : ConstraintPosition() { + lateinit var inputTypePositions: Set + + override fun toString() = + "Incorporate $initialConstraint from position $from" } class CoroutinePosition() : ConstraintPosition() { diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index 64c8895d1ea..34e827eeee0 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -12,9 +12,7 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.TypeConstructorMarker import org.jetbrains.kotlin.types.model.TypeVariableMarker -import org.jetbrains.kotlin.types.TypeConstructor import org.jetbrains.kotlin.types.UnwrappedType -import org.jetbrains.kotlin.types.checker.NewCapturedType import org.jetbrains.kotlin.types.typeUtil.unCapture import kotlin.collections.ArrayList import kotlin.collections.LinkedHashMap @@ -34,14 +32,16 @@ class MutableVariableWithConstraints( // see @OnlyInputTypes annotation val projectedInputCallTypes: Collection - get() = - mutableConstraints.filter { - val position = it.position.from - position is ArgumentConstraintPosition || position is ReceiverConstraintPosition || - position is ExpectedTypeConstraintPosition || position is ExplicitTypeParameterConstraintPosition - }.map { - (it.type as KotlinType).unCapture().unwrap() - } + get() = mutableConstraints + .filter { it.position.isInputTypePosition } + .map { (it.type as KotlinType).unCapture().unwrap() } + + private val ConstraintPosition?.isInputTypePosition: Boolean + get() = when (this) { + is OnlyInputTypeConstraintPosition -> true + !is IncorporationConstraintPosition -> false + else -> from.isInputTypePosition || inputTypePositions.any { it.isInputTypePosition } + } private val mutableConstraints = ArrayList(constraints) diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index 43f20f975bd..f48df413dfe 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -298,10 +298,10 @@ class NewConstraintSystemImpl( variableWithConstraints: MutableVariableWithConstraints?, resultType: KotlinTypeMarker ) { - if (resultType !is KotlinType) return - if (variableWithConstraints == null || variableWithConstraints.typeVariable.safeAs()?.hasOnlyInputTypesAnnotation() != true ) return - val projectedInputCallTypes = variableWithConstraints.projectedInputCallTypes - val resultTypeIsInputType = projectedInputCallTypes.any { inputType -> + if (resultType !is KotlinType || variableWithConstraints == null) return + if (variableWithConstraints.typeVariable.safeAs()?.hasOnlyInputTypesAnnotation() != true) return + + val resultTypeIsInputType = variableWithConstraints.projectedInputCallTypes.any { inputType -> NewKotlinTypeChecker.Default.equalTypes(resultType, inputType) || inputType.constructor is IntersectionTypeConstructor && inputType.constructor.supertypes.any { NewKotlinTypeChecker.Default.equalTypes(resultType, it) } diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.kt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.kt new file mode 100644 index 00000000000..ab8fc179b55 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.kt @@ -0,0 +1,54 @@ +// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE +// !WITH_NEW_INFERENCE + +@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") + +import kotlin.internal.OnlyInputTypes + +interface Bound +class First : Bound +class Second : Bound +class Inv(val v: I) +class InvB(val v: I) +class In(v: C) +class InB(v: C) +class Out(val v: O) +class OutB(val v: O) + +fun <@OnlyInputTypes M> strictId(arg: M): M = arg +fun <@OnlyInputTypes S> strictSelect(arg1: S, arg2: S): S = arg1 + +fun testOK(first: First, bound: Bound, second: Second) { + strictId(Inv(15)) + strictId(Inv("foo")) + strictId(Inv(first)) + strictId(InvB(first)) + strictId(In(first)) + strictId(InB(first)) + strictId(Out(first)) + strictId(OutB(first)) + strictId(Inv(Inv(Inv(first)))) + + strictSelect(Inv(first), Inv(first)) + strictSelect(InvB(first), InvB(first)) + + strictSelect(Out(first), Out(bound)) + strictSelect(OutB(first), OutB(bound)) + strictSelect(In(first), In(bound)) + strictSelect(InB(first), InB(bound)) + strictSelect(InB(first), InB(second)) // different behaviour in contravariant position + + val out: Out = strictSelect(Out(first), Out(second)) + val outb: OutB = strictSelect(OutB(first), OutB(second)) + strictSelect>(Out(first), Out(second)) + strictSelect>(OutB(first), OutB(second)) +} + +fun testFail(first: First, bound: Bound, second: Second) { + strictSelect(InvB(first), InvB(bound)) + strictSelect(Inv(first), Inv(bound)) + strictSelect(Out(first), Out(second)) + strictSelect(In(first), In(second)) + strictSelect(Out(Inv(first)), Out(Inv(second))) + strictSelect(In(Inv(first)), In(Inv(second))) +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.txt b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.txt new file mode 100644 index 00000000000..ae672ea64d9 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.txt @@ -0,0 +1,72 @@ +package + +public fun strictId(/*0*/ arg: M): M +public fun strictSelect(/*0*/ arg1: S, /*1*/ arg2: S): S +public fun testFail(/*0*/ first: First, /*1*/ bound: Bound, /*2*/ second: Second): kotlin.Unit +public fun testOK(/*0*/ first: First, /*1*/ bound: Bound, /*2*/ second: Second): kotlin.Unit + +public interface Bound { + 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 +} + +public final class First : Bound { + public constructor First() + 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 +} + +public final class In { + public constructor In(/*0*/ v: C) + 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 +} + +public final class InB { + public constructor InB(/*0*/ v: C) + 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 +} + +public final class Inv { + public constructor Inv(/*0*/ v: I) + public final val v: I + 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 +} + +public final class InvB { + public constructor InvB(/*0*/ v: I) + public final val v: I + 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 +} + +public final class Out { + public constructor Out(/*0*/ v: O) + public final val v: O + 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 +} + +public final class OutB { + public constructor OutB(/*0*/ v: O) + public final val v: O + 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 +} + +public final class Second : Bound { + public constructor Second() + 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/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 2f5c5672487..23186e5cc0f 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -2843,6 +2843,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCaptured.kt"); } + @TestMetadata("onlyInputTypesCommonConstraintSystem.kt") + public void testOnlyInputTypesCommonConstraintSystem() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.kt"); + } + @TestMetadata("onlyInputTypesWithVarargs.kt") public void testOnlyInputTypesWithVarargs() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesWithVarargs.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index cc7bcba8472..a30c749e549 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -2843,6 +2843,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCaptured.kt"); } + @TestMetadata("onlyInputTypesCommonConstraintSystem.kt") + public void testOnlyInputTypesCommonConstraintSystem() throws Exception { + runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesCommonConstraintSystem.kt"); + } + @TestMetadata("onlyInputTypesWithVarargs.kt") public void testOnlyInputTypesWithVarargs() throws Exception { runTest("compiler/testData/diagnostics/testsWithStdLib/inference/annotationsForResolve/onlyInputTypesWithVarargs.kt");