diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt index ecea70aeaf2..cf517fe2b48 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/components/CheckArgumentsResolutionPart.kt @@ -19,18 +19,21 @@ package org.jetbrains.kotlin.resolve.calls.components import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.builtins.isFunctionType -import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.model.ArgumentConstraintPosition import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.tower.isSuccess import org.jetbrains.kotlin.types.UnwrappedType +import org.jetbrains.kotlin.types.checker.captureFromExpression +import org.jetbrains.kotlin.types.checker.hasSupertypeWithGivenTypeConstructor import org.jetbrains.kotlin.types.checker.intersectWrappedTypes +import org.jetbrains.kotlin.types.lowerIfFlexible import org.jetbrains.kotlin.types.typeUtil.builtIns +import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes import org.jetbrains.kotlin.types.typeUtil.supertypes +import org.jetbrains.kotlin.types.upperIfFlexible import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.addIfNotNull import java.lang.UnsupportedOperationException @@ -202,7 +205,7 @@ internal fun checkExpressionArgument( isReceiver: Boolean ): KotlinCallDiagnostic? { // todo run this approximation only once for call - val argumentType = expressionArgument.stableType + val argumentType = captureFromTypeParameterUpperBoundIfNeeded(expressionArgument.stableType, expectedType) fun unstableSmartCastOrSubtypeError( unstableType: UnwrappedType?, expectedType: UnwrappedType, position: ArgumentConstraintPosition @@ -246,6 +249,38 @@ internal fun checkExpressionArgument( return null } +/** + * interface Inv + * fun bar(l: Inv): Y = ... + * + * fun > foo(x: X) { + * val xr = bar(x) + * } + * Here we try to capture from upper bound from type parameter. + * We replace type of `x` to `Inv`(we chose supertype which contains supertype with expectedTypeConstructor) and capture from this type. + * It is correct, because it is like this code: + * fun > foo(x: X) { + * val inv: Inv = x + * val xr = bar(inv) + * } + * + */ +private fun captureFromTypeParameterUpperBoundIfNeeded(argumentType: UnwrappedType, expectedType: UnwrappedType): UnwrappedType { + val expectedTypeConstructor = expectedType.upperIfFlexible().constructor + + if (argumentType.lowerIfFlexible().constructor.declarationDescriptor is TypeParameterDescriptor) { + val chosenSupertype = argumentType.lowerIfFlexible().supertypes().singleOrNull { + it.constructor.declarationDescriptor is ClassifierDescriptorWithTypeParameters && + it.unwrap().hasSupertypeWithGivenTypeConstructor(expectedTypeConstructor) + } + if (chosenSupertype != null) { + return captureFromExpression(chosenSupertype.unwrap()) ?: argumentType + } + } + + return argumentType +} + // if expression is not stable and has smart casts, then we create this type private val ExpressionKotlinCallArgument.unstableType: UnwrappedType? get() { diff --git a/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.kt b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.kt new file mode 100644 index 00000000000..603295865f2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.kt @@ -0,0 +1,11 @@ +interface Inv + +fun > foo(x: X, y: Y) { + val rX = bar(x) + rX.length + + val rY = bar(y) + rY.length +} + +fun bar(l: Inv): Y = TODO() \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.txt b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.txt new file mode 100644 index 00000000000..b2ac7794784 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.txt @@ -0,0 +1,10 @@ +package + +public fun bar(/*0*/ l: Inv): Y +public fun > foo(/*0*/ x: X, /*1*/ y: Y): kotlin.Unit + +public 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index e6d3a70f706..a4ca4d57edf 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -10236,6 +10236,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("captureFromTypeParameterUpperBound.kt") + public void testCaptureFromTypeParameterUpperBound() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/captureFromTypeParameterUpperBound.kt"); + doTest(fileName); + } + @TestMetadata("captureTypeOnlyOnTopLevel.kt") public void testCaptureTypeOnlyOnTopLevel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/captureTypeOnlyOnTopLevel.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt index 9546d45e3b8..5037de9c01a 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/checker/NewKotlinTypeChecker.kt @@ -391,6 +391,9 @@ object NullabilityChecker { } +fun UnwrappedType.hasSupertypeWithGivenTypeConstructor(typeConstructor: TypeConstructor) = + TypeCheckerContext(false).anySupertype(lowerIfFlexible(), { it.constructor == typeConstructor }, { SupertypesPolicy.LowerIfFlexible }) + /** * ClassType means that type constructor for this type is type for real class or interface */