From 6ce8d661ad9f16f0e8d11db0420442b315996ac0 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Fri, 20 Mar 2020 15:32:33 +0300 Subject: [PATCH] [FIR] Add nullability to ILT. #KT-37639 Fixed --- .../inference/nullableIntegerLiteralType.kt | 13 +++++++++ .../inference/nullableIntegerLiteralType.txt | 27 +++++++++++++++++++ .../resolve/samConversions/genericSam.txt | 4 +-- .../j+k/FunctionTypeInJava.txt | 2 +- .../fir/FirDiagnosticsTestGenerated.java | 5 ++++ ...DiagnosticsWithLightTreeTestGenerated.java | 5 ++++ .../jetbrains/kotlin/fir/types/ConeTypes.kt | 6 +++-- .../jetbrains/kotlin/fir/types/TypeUtils.kt | 20 +++++++------- .../fir/types/ConeIntegerLiteralTypeImpl.kt | 23 +++++++++++++--- .../codegen/box/boxingOptimization/kt5588.kt | 1 - .../expressions/whenUnusedExpression.fir.txt | 6 ++--- 11 files changed, 89 insertions(+), 23 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.txt diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.kt b/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.kt new file mode 100644 index 00000000000..2bfbe8fa5ea --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.kt @@ -0,0 +1,13 @@ +// ISSUE: KT-37639 + +fun takeInt(x: Int) {} + +fun test_1(b: Boolean) { + val x = if (b) 1 else null + takeInt(x) +} + +fun test_2(b: Boolean, y: Int) { + val x = if (b) y else null + takeInt(x) +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.txt b/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.txt new file mode 100644 index 00000000000..c80de5e09a6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.txt @@ -0,0 +1,27 @@ +FILE: nullableIntegerLiteralType.kt + public final fun takeInt(x: R|kotlin/Int|): R|kotlin/Unit| { + } + public final fun test_1(b: R|kotlin/Boolean|): R|kotlin/Unit| { + lval x: R|kotlin/Int?| = when () { + R|/b| -> { + Int(1) + } + else -> { + Null(null) + } + } + + #(R|/x|) + } + public final fun test_2(b: R|kotlin/Boolean|, y: R|kotlin/Int|): R|kotlin/Unit| { + lval x: R|kotlin/Int?| = when () { + R|/b| -> { + R|/y| + } + else -> { + Null(null) + } + } + + #(R|/x|) + } diff --git a/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.txt b/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.txt index 39caae7fff8..d170c3508e5 100644 --- a/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.txt +++ b/compiler/fir/analysis-tests/testData/resolve/samConversions/genericSam.txt @@ -12,11 +12,11 @@ FILE: main.kt ^ R|/x|.R|kotlin/Any.toString|() } ) - Q|JavaUsage|.R|/JavaUsage.foo3|!|>(foo3@fun (x: R|ft!|): R|kotlin/String| { + Q|JavaUsage|.R|/JavaUsage.foo3|!|, R|ft!|>(foo3@fun (x: R|ft!|): R|kotlin/String| { ^ R|/x|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|() } , Int(1)) - Q|JavaUsage|.R|/JavaUsage.foo3|!|>(foo3@fun (x: R|kotlin/Number|): R|kotlin/String| { + Q|JavaUsage|.R|/JavaUsage.foo3|!|, R|ft!|>(foo3@fun (x: R|kotlin/Number|): R|kotlin/String| { ^ R|/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|() } , Int(2)) diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/FunctionTypeInJava.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/FunctionTypeInJava.txt index b0e5c16b064..a9aed2b659c 100644 --- a/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/FunctionTypeInJava.txt +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k/FunctionTypeInJava.txt @@ -17,7 +17,7 @@ FILE: main.kt } Q|JavaClass|.R|/JavaClass.foo2|(R|/y|) - Q|JavaClass|.R|/JavaClass.foo3|(foo3@fun (it: R|ft!|): R|ft!| { + Q|JavaClass|.R|/JavaClass.foo3|!|>(foo3@fun (it: R|ft!|): R|ft!| { ^ R|/it|.R|kotlin/Int.plus|(Int(4)).R|kotlin/Any.toString|() } , Int(5)) 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 8b9fcdcb513..e499306b6d9 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 @@ -1288,6 +1288,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/inference/nestedLambdas.kt"); } + @TestMetadata("nullableIntegerLiteralType.kt") + public void testNullableIntegerLiteralType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.kt"); + } + @TestMetadata("receiverWithCapturedType.kt") public void testReceiverWithCapturedType() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/inference/receiverWithCapturedType.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 4e87d429e12..16606de8a5c 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 @@ -1288,6 +1288,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/inference/nestedLambdas.kt"); } + @TestMetadata("nullableIntegerLiteralType.kt") + public void testNullableIntegerLiteralType() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/inference/nullableIntegerLiteralType.kt"); + } + @TestMetadata("receiverWithCapturedType.kt") public void testReceiverWithCapturedType() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/inference/receiverWithCapturedType.kt"); diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt index f92a94e90e1..11ddb47951b 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeTypes.kt @@ -219,12 +219,14 @@ class ConeTypeVariableTypeConstructor(val debugName: String) : ConeClassifierLoo override val name: Name get() = Name.identifier(debugName) } -abstract class ConeIntegerLiteralType(val value: Long) : ConeSimpleKotlinType(), TypeConstructorMarker { +abstract class ConeIntegerLiteralType( + val value: Long, + override val nullability: ConeNullability +) : ConeSimpleKotlinType(), TypeConstructorMarker { abstract val possibleTypes: Collection abstract val supertypes: List override val typeArguments: Array = emptyArray() - override val nullability: ConeNullability = ConeNullability.NOT_NULL abstract fun getApproximatedType(expectedType: ConeKotlinType? = null): ConeClassLikeType diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt index 30a129e1421..71d27fd3591 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/types/TypeUtils.kt @@ -121,34 +121,34 @@ fun T.withNullability(nullability: ConeNullability, typeCon return when (this) { is ConeClassErrorType -> this - is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable) as T - is ConeTypeParameterTypeImpl -> ConeTypeParameterTypeImpl(lookupTag, nullability.isNullable) as T + is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable) + is ConeTypeParameterTypeImpl -> ConeTypeParameterTypeImpl(lookupTag, nullability.isNullable) is ConeFlexibleType -> { if (nullability == ConeNullability.UNKNOWN) { if (lowerBound.nullability != upperBound.nullability || lowerBound.nullability == ConeNullability.UNKNOWN) { return this } } - coneFlexibleOrSimpleType(typeContext, lowerBound.withNullability(nullability), upperBound.withNullability(nullability)) as T + coneFlexibleOrSimpleType(typeContext, lowerBound.withNullability(nullability), upperBound.withNullability(nullability)) } - is ConeTypeVariableType -> ConeTypeVariableType(nullability, lookupTag) as T - is ConeCapturedType -> ConeCapturedType(captureStatus, lowerType, nullability, constructor) as T + is ConeTypeVariableType -> ConeTypeVariableType(nullability, lookupTag) + is ConeCapturedType -> ConeCapturedType(captureStatus, lowerType, nullability, constructor) is ConeIntersectionType -> when (nullability) { ConeNullability.NULLABLE -> this.mapTypes { it.withNullability(nullability) } ConeNullability.UNKNOWN -> this // TODO: is that correct? ConeNullability.NOT_NULL -> this - } as T - is ConeStubType -> ConeStubType(variable, nullability) as T + } + is ConeStubType -> ConeStubType(variable, nullability) is ConeDefinitelyNotNullType -> when (nullability) { ConeNullability.NOT_NULL -> this ConeNullability.NULLABLE -> original.withNullability(nullability) ConeNullability.UNKNOWN -> original.withNullability(nullability) - } as T - is ConeIntegerLiteralType -> this + } + is ConeIntegerLiteralType -> ConeIntegerLiteralTypeImpl(value, nullability) else -> error("sealed: ${this::class}") - } + } as T } fun coneFlexibleOrSimpleType( diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/ConeIntegerLiteralTypeImpl.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/ConeIntegerLiteralTypeImpl.kt index da6d6a2183b..d6e92a2a6d2 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/ConeIntegerLiteralTypeImpl.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/types/ConeIntegerLiteralTypeImpl.kt @@ -14,7 +14,7 @@ import org.jetbrains.kotlin.types.model.SimpleTypeMarker class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType { override val possibleTypes: Collection - constructor(value: Long) : super(value) { + constructor(value: Long, nullability: ConeNullability = ConeNullability.NOT_NULL) : super(value, nullability) { possibleTypes = mutableListOf() fun checkBoundsAndAddPossibleType(classId: ClassId, range: LongRange) { @@ -34,7 +34,11 @@ class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType { // TODO: add support of unsigned types } - private constructor(value: Long, possibleTypes: Collection) : super(value) { + private constructor( + value: Long, + possibleTypes: Collection, + nullability: ConeNullability = ConeNullability.NOT_NULL + ) : super(value, nullability) { this.possibleTypes = possibleTypes } @@ -46,10 +50,11 @@ class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType { } override fun getApproximatedType(expectedType: ConeKotlinType?): ConeClassLikeType { - return when (expectedType) { + val approximatedType = when (expectedType) { null, !in possibleTypes -> possibleTypes.first() else -> expectedType as ConeClassLikeType } + return approximatedType.withNullability(nullability) } companion object { @@ -121,4 +126,14 @@ fun ConeKotlinType.approximateIntegerLiteralType(expectedType: ConeKotlinType? = (this as? ConeIntegerLiteralType)?.getApproximatedType(expectedType) ?: this fun ConeKotlinType.approximateIntegerLiteralTypeOrNull(expectedType: ConeKotlinType? = null): ConeKotlinType? = - (this as? ConeIntegerLiteralType)?.getApproximatedType(expectedType) \ No newline at end of file + (this as? ConeIntegerLiteralType)?.getApproximatedType(expectedType) + +private fun ConeClassLikeType.withNullability(nullability: ConeNullability): ConeClassLikeType { + if (nullability == this.nullability) return this + + return when (this) { + is ConeClassErrorType -> this + is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable) + else -> error("sealed") + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/boxingOptimization/kt5588.kt b/compiler/testData/codegen/box/boxingOptimization/kt5588.kt index d1c0319ee4a..ece5914c504 100644 --- a/compiler/testData/codegen/box/boxingOptimization/kt5588.kt +++ b/compiler/testData/codegen/box/boxingOptimization/kt5588.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR fun box() : String { val s = "notA" val id = when (s) { diff --git a/compiler/testData/ir/irText/expressions/whenUnusedExpression.fir.txt b/compiler/testData/ir/irText/expressions/whenUnusedExpression.fir.txt index 72b8ef480c9..93b136c175f 100644 --- a/compiler/testData/ir/irText/expressions/whenUnusedExpression.fir.txt +++ b/compiler/testData/ir/irText/expressions/whenUnusedExpression.fir.txt @@ -3,13 +3,13 @@ FILE fqName: fileName:/whenUnusedExpression.kt VALUE_PARAMETER name:b index:0 type:kotlin.Boolean VALUE_PARAMETER name:i index:1 type:kotlin.Int BLOCK_BODY - WHEN type=kotlin.Int origin=IF + WHEN type=kotlin.Int? origin=IF BRANCH if: GET_VAR 'b: kotlin.Boolean declared in .test' type=kotlin.Boolean origin=null - then: BLOCK type=kotlin.Int origin=WHEN + then: BLOCK type=kotlin.Int? origin=WHEN VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:kotlin.Int [val] GET_VAR 'i: kotlin.Int declared in .test' type=kotlin.Int origin=null - WHEN type=kotlin.Int origin=WHEN + WHEN type=kotlin.Int? origin=WHEN BRANCH if: CALL 'public final fun EQEQ (arg0: kotlin.Any?, arg1: kotlin.Any?): kotlin.Boolean declared in kotlin.internal.ir' type=kotlin.Boolean origin=EQEQ arg0: GET_VAR 'val tmp_0: kotlin.Int [val] declared in .test' type=kotlin.Int origin=null