[FIR] Capture type from type parameter upper bound when needed

This commit is contained in:
Dmitriy Novozhilov
2020-09-25 16:00:48 +03:00
parent 393688ad82
commit cc4f72e032
7 changed files with 92 additions and 3 deletions
@@ -0,0 +1,12 @@
// ISSUE: KT-40131
import kotlin.reflect.KClass
val <U> KClass<U>.javaImpl: Class<U>
get() = null!!
val <T : KClass<*>> T.myJava1: Class<*>
get() = <!DEBUG_INFO_EXPRESSION_TYPE("java.lang.Class<out kotlin.Any?>")!>javaImpl<!>
val <E : Any, T : KClass<E>> T.myJava2: Class<E>
get() = <!DEBUG_INFO_EXPRESSION_TYPE("java.lang.Class<E>")!>javaImpl<!>
@@ -0,0 +1,13 @@
FILE: kt40131.kt
public final val <U> R|kotlin/reflect/KClass<U>|.javaImpl: R|java/lang/Class<U>|
public get(): R|java/lang/Class<U>| {
^ Null(null)!!
}
public final val <T : R|kotlin/reflect/KClass<*>|> R|T|.myJava1: R|java/lang/Class<*>|
public get(): R|java/lang/Class<*>| {
^ this@R|/myJava1|.R|/javaImpl|<R|kotlin/Any?|>
}
public final val <E : R|kotlin/Any|, T : R|kotlin/reflect/KClass<E>|> R|T|.myJava2: R|java/lang/Class<E>|
public get(): R|java/lang/Class<E>| {
^ this@R|/myJava2|.R|/javaImpl|<R|E|>
}
@@ -1744,6 +1744,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/intersectionTypesInConstraints.kt");
}
@TestMetadata("kt40131.kt")
public void testKt40131() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/kt40131.kt");
}
@TestMetadata("kt41989.kt")
public void testKt41989() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/kt41989.kt");
@@ -1744,6 +1744,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/inference/intersectionTypesInConstraints.kt");
}
@TestMetadata("kt40131.kt")
public void testKt40131() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/kt40131.kt");
}
@TestMetadata("kt41989.kt")
public void testKt41989() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/kt41989.kt");
@@ -15,9 +15,12 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.firUnsafe
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
import org.jetbrains.kotlin.fir.resolve.transformers.ensureResolvedTypeDeclaration
import org.jetbrains.kotlin.fir.returnExpressions
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
import org.jetbrains.kotlin.fir.typeCheckerContext
import org.jetbrains.kotlin.fir.typeContext
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.addSubtypeConstraintIfCompatible
import org.jetbrains.kotlin.resolve.calls.inference.model.SimpleConstraintSystemConstraintPosition
@@ -408,3 +411,49 @@ internal fun FirExpression.getExpectedType(
fun ConeKotlinType.varargElementType(): ConeKotlinType {
return this.arrayElementType() ?: this
}
/**
* 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)
* }
*
*/
internal fun captureFromTypeParameterUpperBoundIfNeeded(
argumentType: ConeKotlinType,
expectedType: ConeKotlinType,
session: FirSession
): ConeKotlinType {
val expectedTypeClassId = expectedType.upperBoundIfFlexible().classId ?: return argumentType
val simplifiedArgumentType = argumentType.lowerBoundIfFlexible() as? ConeTypeParameterType ?: return argumentType
val typeParameter = simplifiedArgumentType.lookupTag.typeParameterSymbol.fir
val context = session.typeCheckerContext
val chosenSupertype = typeParameter.bounds.map { it.coneType }
.singleOrNull { it.hasSupertypeWithGivenClassId(expectedTypeClassId, context) } ?: return argumentType
val capturedType = context.captureFromExpression(chosenSupertype) as ConeKotlinType? ?: return argumentType
return if (argumentType is ConeDefinitelyNotNullType) {
ConeDefinitelyNotNullType.create(capturedType) ?: capturedType
} else {
capturedType
}
}
private fun ConeKotlinType.hasSupertypeWithGivenClassId(classId: ClassId, context: ConeTypeCheckerContext): Boolean {
return with(context) {
anySuperTypeConstructor {
it is ConeClassLikeLookupTag && it.classId == classId
}
}
}
@@ -130,10 +130,16 @@ internal sealed class CheckReceivers : ResolutionStage() {
} else {
val argumentExtensionReceiverValue = candidate.implicitExtensionReceiverValue
if (argumentExtensionReceiverValue != null && explicitReceiverKind.shouldBeCheckedAgainstImplicit()) {
val expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverType.type)
val argumentType = captureFromTypeParameterUpperBoundIfNeeded(
argumentType = argumentExtensionReceiverValue.type,
expectedType = expectedType,
session = context.session
)
candidate.resolvePlainArgumentType(
candidate.csBuilder,
argumentType = argumentExtensionReceiverValue.type,
expectedType = candidate.substitutor.substituteOrSelf(expectedReceiverType.type),
argumentType = argumentType,
expectedType = expectedType,
sink = sink,
context = context,
isReceiver = true,
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// IGNORE_BACKEND: JS_IR
// IGNORE_BACKEND: JS_IR_ES6
// TODO: muted automatically, investigate should it be ran for JS or not