[FIR] fix subtyping for nullable captured types.

The issue is the type checker doesn't consider P? a subtype of
CapturedType<in P>?, whereas P a subtype of CapturedType<in P>?. In
AbstractTypeCheckerContext::checkSubtypeForSpecialCases, it checks if
P? is a subtype of the lower type of the captured type, which is
P, and returns false.

This fix uses nullable version of the lower type when the captured
type is marked nullable. To check if P? is a subtype of Captured<in
P>?, we check the LHS, P?, against the nullable lower type of RHS,
P?.

^KT-42825 Fixed
This commit is contained in:
Juan Chen
2020-10-26 18:06:33 +00:00
committed by Mikhail Glukhikh
parent d96223a2ff
commit eb804709da
11 changed files with 74 additions and 6 deletions
@@ -16041,6 +16041,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt");
}
@TestMetadata("kt42825.kt")
public void testKt42825() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt");
}
@TestMetadata("propertyVarianceConflict.kt")
public void testPropertyVarianceConflict() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt");
@@ -39,6 +39,13 @@ open class IrTypeCheckerContext(override val irBuiltIns: IrBuiltIns) : IrTypeSys
): AbstractTypeCheckerContext = IrTypeCheckerContext(irBuiltIns)
override fun KotlinTypeMarker.isUninferredParameter(): Boolean = false
override fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker {
if (this.isSimpleType()) {
return this.asSimpleType()!!.withNullability(nullable)
} else {
error("withNullability for non-simple types is not supported in IR")
}
}
override fun captureFromExpression(type: KotlinTypeMarker): KotlinTypeMarker? =
error("Captured type is unsupported in IR")
@@ -0,0 +1,23 @@
// FILE: Processor.java
public interface Processor<T> {
boolean process(T t);
}
// FILE: test.kt
interface PsiModifierListOwner
interface KtClassOrObject {
fun toLightClass(): PsiModifierListOwner?
}
fun execute(declaration: Any, consumer: Processor<in PsiModifierListOwner>) {
when (declaration) {
is KtClassOrObject -> {
val lightClass = declaration.toLightClass()
consumer.process(lightClass)
}
}
}
fun box(): String = "OK"
@@ -17441,6 +17441,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt");
}
@TestMetadata("kt42825.kt")
public void testKt42825() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt");
}
@TestMetadata("propertyVarianceConflict.kt")
public void testPropertyVarianceConflict() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt");
@@ -17441,6 +17441,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt");
}
@TestMetadata("kt42825.kt")
public void testKt42825() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt");
}
@TestMetadata("propertyVarianceConflict.kt")
public void testPropertyVarianceConflict() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt");
@@ -16041,6 +16041,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/javaInterop/generics/javaNestedSamInterface.kt");
}
@TestMetadata("kt42825.kt")
public void testKt42825() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt");
}
@TestMetadata("propertyVarianceConflict.kt")
public void testPropertyVarianceConflict() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/propertyVarianceConflict.kt");
@@ -407,9 +407,15 @@ object AbstractTypeChecker {
val superTypeCaptured = superType.asCapturedType()
val lowerType = superTypeCaptured?.lowerType()
if (superTypeCaptured != null && lowerType != null) {
// If superType is nullable, e.g., to check if Foo? a subtype of Captured<in Foo>?, we check the LHS, Foo?,
// against the nullable version of the lower type of RHS. See KT-42825
val nullableLowerType =
if (superType.isMarkedNullable())
lowerType.withNullability(true)
else lowerType
when (getLowerCapturedTypePolicy(subType, superTypeCaptured)) {
CHECK_ONLY_LOWER -> return isSubtypeOf(this, subType, lowerType)
CHECK_SUBTYPE_AND_LOWER -> if (isSubtypeOf(this, subType, lowerType)) return true
CHECK_ONLY_LOWER -> return isSubtypeOf(this, subType, nullableLowerType)
CHECK_SUBTYPE_AND_LOWER -> if (isSubtypeOf(this, subType, nullableLowerType)) return true
SKIP_LOWER -> Unit
}
}
@@ -144,9 +144,6 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
fun KotlinTypeMarker.isBuiltinFunctionalTypeOrSubtype(): Boolean
fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker
fun KotlinTypeMarker.makeDefinitelyNotNullOrNotNull(): KotlinTypeMarker
fun SimpleTypeMarker.makeSimpleTypeDefinitelyNotNullOrNotNull(): SimpleTypeMarker
@@ -241,7 +238,6 @@ interface TypeSystemContext : TypeSystemOptimizationContext {
fun KotlinTypeMarker.isError(): Boolean
fun TypeConstructorMarker.isError(): Boolean
fun KotlinTypeMarker.isUninferredParameter(): Boolean
fun FlexibleTypeMarker.asDynamicType(): DynamicTypeMarker?
fun FlexibleTypeMarker.asRawType(): RawTypeMarker?
@@ -259,6 +255,7 @@ interface TypeSystemContext : TypeSystemOptimizationContext {
fun SimpleTypeMarker.withNullability(nullable: Boolean): SimpleTypeMarker
fun SimpleTypeMarker.typeConstructor(): TypeConstructorMarker
fun KotlinTypeMarker.withNullability(nullable: Boolean): KotlinTypeMarker
fun CapturedTypeMarker.typeConstructor(): CapturedTypeConstructorMarker
fun CapturedTypeMarker.captureStatus(): CaptureStatus
@@ -13895,6 +13895,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
public void testAllFilesPresentInGenerics() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaInterop/generics"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true);
}
@TestMetadata("kt42825.kt")
public void testKt42825() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/javaInterop/notNullAssertions")
@@ -13895,6 +13895,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
public void testAllFilesPresentInGenerics() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaInterop/generics"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true);
}
@TestMetadata("kt42825.kt")
public void testKt42825() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/javaInterop/notNullAssertions")
@@ -13960,6 +13960,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
public void testAllFilesPresentInGenerics() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/javaInterop/generics"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true);
}
@TestMetadata("kt42825.kt")
public void testKt42825() throws Exception {
runTest("compiler/testData/codegen/box/javaInterop/generics/kt42825.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/javaInterop/notNullAssertions")