diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/kt40131.kt b/compiler/fir/analysis-tests/testData/resolve/inference/kt40131.kt new file mode 100644 index 00000000000..5f0d88a4ab5 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/inference/kt40131.kt @@ -0,0 +1,12 @@ +// ISSUE: KT-40131 + +import kotlin.reflect.KClass + +val KClass.javaImpl: Class + get() = null!! + +val > T.myJava1: Class<*> + get() = ")!>javaImpl + +val > T.myJava2: Class + get() = ")!>javaImpl diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/kt40131.txt b/compiler/fir/analysis-tests/testData/resolve/inference/kt40131.txt new file mode 100644 index 00000000000..416747f45b3 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/inference/kt40131.txt @@ -0,0 +1,13 @@ +FILE: kt40131.kt + public final val R|kotlin/reflect/KClass|.javaImpl: R|java/lang/Class| + public get(): R|java/lang/Class| { + ^ Null(null)!! + } + public final val |> R|T|.myJava1: R|java/lang/Class<*>| + public get(): R|java/lang/Class<*>| { + ^ this@R|/myJava1|.R|/javaImpl| + } + public final val |> R|T|.myJava2: R|java/lang/Class| + public get(): R|java/lang/Class| { + ^ this@R|/myJava2|.R|/javaImpl| + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 00c706e3f4e..1fdbe9d31dd 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -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"); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 40dfc73e8a0..c5f8b10bd49 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -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"); diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index 4782039bdf2..219609b3f83 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.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 + * 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) + * } + * + */ +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 + } + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt index 3a719cf1528..12c8b316a58 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ResolutionStages.kt @@ -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, diff --git a/compiler/testData/codegen/box/classLiteral/java/kt11943.kt b/compiler/testData/codegen/box/classLiteral/java/kt11943.kt index e2a8e305ca6..33a76473834 100644 --- a/compiler/testData/codegen/box/classLiteral/java/kt11943.kt +++ b/compiler/testData/codegen/box/classLiteral/java/kt11943.kt @@ -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