[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:
Stanislav Erokhin
2017-04-06 22:17:18 +03:00
parent c4ddc7a3a6
commit b4bf534d29
5 changed files with 69 additions and 4 deletions
@@ -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<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
private val ExpressionKotlinCallArgument.unstableType: UnwrappedType?
get() {
@@ -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()
@@ -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);
}
@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");
@@ -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
*/