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");