[NI] Capture from supertypes of type parameter.
This works by accident in OI, so this should be supported by new inference.
This commit is contained in:
+39
-4
@@ -19,18 +19,21 @@ package org.jetbrains.kotlin.resolve.calls.components
|
|||||||
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
|
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
|
||||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
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.ArgumentConstraintPosition
|
||||||
import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable
|
import org.jetbrains.kotlin.resolve.calls.inference.model.LambdaTypeVariable
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||||
import org.jetbrains.kotlin.types.UnwrappedType
|
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.checker.intersectWrappedTypes
|
||||||
|
import org.jetbrains.kotlin.types.lowerIfFlexible
|
||||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
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.typeUtil.supertypes
|
||||||
|
import org.jetbrains.kotlin.types.upperIfFlexible
|
||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import java.lang.UnsupportedOperationException
|
import java.lang.UnsupportedOperationException
|
||||||
@@ -202,7 +205,7 @@ internal fun checkExpressionArgument(
|
|||||||
isReceiver: Boolean
|
isReceiver: Boolean
|
||||||
): KotlinCallDiagnostic? {
|
): KotlinCallDiagnostic? {
|
||||||
// todo run this approximation only once for call
|
// todo run this approximation only once for call
|
||||||
val argumentType = expressionArgument.stableType
|
val argumentType = captureFromTypeParameterUpperBoundIfNeeded(expressionArgument.stableType, expectedType)
|
||||||
|
|
||||||
fun unstableSmartCastOrSubtypeError(
|
fun unstableSmartCastOrSubtypeError(
|
||||||
unstableType: UnwrappedType?, expectedType: UnwrappedType, position: ArgumentConstraintPosition
|
unstableType: UnwrappedType?, expectedType: UnwrappedType, position: ArgumentConstraintPosition
|
||||||
@@ -246,6 +249,38 @@ internal fun checkExpressionArgument(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interface Inv<T>
|
||||||
|
* fun <Y> bar(l: Inv<Y>): Y = ...
|
||||||
|
*
|
||||||
|
* fun <X : Inv<out Int>> 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<out Int>`(we chose supertype which contains supertype with expectedTypeConstructor) and capture from this type.
|
||||||
|
* It is correct, because it is like this code:
|
||||||
|
* fun <X : Inv<out Int>> foo(x: X) {
|
||||||
|
* val inv: Inv<out Int> = 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
|
// if expression is not stable and has smart casts, then we create this type
|
||||||
private val ExpressionKotlinCallArgument.unstableType: UnwrappedType?
|
private val ExpressionKotlinCallArgument.unstableType: UnwrappedType?
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
interface Inv<T>
|
||||||
|
|
||||||
|
fun <Y: X, X : Inv<out String>> foo(x: X, y: Y) {
|
||||||
|
val rX = bar(x)
|
||||||
|
rX.length
|
||||||
|
|
||||||
|
val rY = bar(y)
|
||||||
|
rY.length
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <Y> bar(<!UNUSED_PARAMETER!>l<!>: Inv<Y>): Y = TODO()
|
||||||
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package
|
||||||
|
|
||||||
|
public fun </*0*/ Y> bar(/*0*/ l: Inv<Y>): Y
|
||||||
|
public fun </*0*/ Y : X, /*1*/ X : Inv<out kotlin.String>> foo(/*0*/ x: X, /*1*/ y: Y): kotlin.Unit
|
||||||
|
|
||||||
|
public interface Inv</*0*/ T> {
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -10236,6 +10236,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("captureTypeOnlyOnTopLevel.kt")
|
||||||
public void testCaptureTypeOnlyOnTopLevel() throws Exception {
|
public void testCaptureTypeOnlyOnTopLevel() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/captureTypeOnlyOnTopLevel.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/inference/capturedTypes/captureTypeOnlyOnTopLevel.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
|
* ClassType means that type constructor for this type is type for real class or interface
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user