[FIR] Add nullability to ILT. #KT-37639 Fixed

This commit is contained in:
Dmitriy Novozhilov
2020-03-20 15:32:33 +03:00
parent 006e1f6528
commit 6ce8d661ad
11 changed files with 89 additions and 23 deletions
@@ -0,0 +1,13 @@
// ISSUE: KT-37639
fun takeInt(x: Int) {}
fun test_1(b: Boolean) {
val x = if (b) 1 else null
<!INAPPLICABLE_CANDIDATE!>takeInt<!>(x)
}
fun test_2(b: Boolean, y: Int) {
val x = if (b) y else null
<!INAPPLICABLE_CANDIDATE!>takeInt<!>(x)
}
@@ -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|<local>/b| -> {
Int(1)
}
else -> {
Null(null)
}
}
<Inapplicable(INAPPLICABLE): [/takeInt]>#(R|<local>/x|)
}
public final fun test_2(b: R|kotlin/Boolean|, y: R|kotlin/Int|): R|kotlin/Unit| {
lval x: R|kotlin/Int?| = when () {
R|<local>/b| -> {
R|<local>/y|
}
else -> {
Null(null)
}
}
<Inapplicable(INAPPLICABLE): [/takeInt]>#(R|<local>/x|)
}
@@ -12,11 +12,11 @@ FILE: main.kt
^ R|<local>/x|.R|kotlin/Any.toString|() ^ R|<local>/x|.R|kotlin/Any.toString|()
} }
) )
Q|JavaUsage|.R|/JavaUsage.foo3|<R|kotlin/Int|, R|ft<kotlin/String, kotlin/String?>!|>(foo3@fun <anonymous>(x: R|ft<kotlin/Int, kotlin/Int?>!|): R|kotlin/String| { Q|JavaUsage|.R|/JavaUsage.foo3|<R|ft<kotlin/Int, kotlin/Int?>!|, R|ft<kotlin/String, kotlin/String?>!|>(foo3@fun <anonymous>(x: R|ft<kotlin/Int, kotlin/Int?>!|): R|kotlin/String| {
^ R|<local>/x|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|() ^ R|<local>/x|.R|kotlin/Int.plus|(Int(1)).R|kotlin/Any.toString|()
} }
, Int(1)) , Int(1))
Q|JavaUsage|.R|/JavaUsage.foo3|<R|kotlin/Int|, R|ft<kotlin/String, kotlin/String?>!|>(foo3@fun <anonymous>(x: R|kotlin/Number|): R|kotlin/String| { Q|JavaUsage|.R|/JavaUsage.foo3|<R|ft<kotlin/Int, kotlin/Int?>!|, R|ft<kotlin/String, kotlin/String?>!|>(foo3@fun <anonymous>(x: R|kotlin/Number|): R|kotlin/String| {
^ R|<local>/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|() ^ R|<local>/x|.R|kotlin/Number.toInt|().R|kotlin/Any.toString|()
} }
, Int(2)) , Int(2))
@@ -17,7 +17,7 @@ FILE: main.kt
} }
Q|JavaClass|.R|/JavaClass.foo2|(R|<local>/y|) Q|JavaClass|.R|/JavaClass.foo2|(R|<local>/y|)
Q|JavaClass|.R|/JavaClass.foo3|<R|kotlin/Int|>(foo3@fun <anonymous>(it: R|ft<kotlin/Int, kotlin/Int?>!|): R|ft<kotlin/String, kotlin/String?>!| { Q|JavaClass|.R|/JavaClass.foo3|<R|ft<kotlin/Int, kotlin/Int?>!|>(foo3@fun <anonymous>(it: R|ft<kotlin/Int, kotlin/Int?>!|): R|ft<kotlin/String, kotlin/String?>!| {
^ R|<local>/it|.R|kotlin/Int.plus|(Int(4)).R|kotlin/Any.toString|() ^ R|<local>/it|.R|kotlin/Int.plus|(Int(4)).R|kotlin/Any.toString|()
} }
, Int(5)) , Int(5))
@@ -1288,6 +1288,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/nestedLambdas.kt"); 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") @TestMetadata("receiverWithCapturedType.kt")
public void testReceiverWithCapturedType() throws Exception { public void testReceiverWithCapturedType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/receiverWithCapturedType.kt"); runTest("compiler/fir/analysis-tests/testData/resolve/inference/receiverWithCapturedType.kt");
@@ -1288,6 +1288,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/inference/nestedLambdas.kt"); 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") @TestMetadata("receiverWithCapturedType.kt")
public void testReceiverWithCapturedType() throws Exception { public void testReceiverWithCapturedType() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/inference/receiverWithCapturedType.kt"); runTest("compiler/fir/analysis-tests/testData/resolve/inference/receiverWithCapturedType.kt");
@@ -219,12 +219,14 @@ class ConeTypeVariableTypeConstructor(val debugName: String) : ConeClassifierLoo
override val name: Name get() = Name.identifier(debugName) 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<ConeClassLikeType> abstract val possibleTypes: Collection<ConeClassLikeType>
abstract val supertypes: List<ConeClassLikeType> abstract val supertypes: List<ConeClassLikeType>
override val typeArguments: Array<out ConeTypeProjection> = emptyArray() override val typeArguments: Array<out ConeTypeProjection> = emptyArray()
override val nullability: ConeNullability = ConeNullability.NOT_NULL
abstract fun getApproximatedType(expectedType: ConeKotlinType? = null): ConeClassLikeType abstract fun getApproximatedType(expectedType: ConeKotlinType? = null): ConeClassLikeType
@@ -121,34 +121,34 @@ fun <T : ConeKotlinType> T.withNullability(nullability: ConeNullability, typeCon
return when (this) { return when (this) {
is ConeClassErrorType -> this is ConeClassErrorType -> this
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable) as T is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable)
is ConeTypeParameterTypeImpl -> ConeTypeParameterTypeImpl(lookupTag, nullability.isNullable) as T is ConeTypeParameterTypeImpl -> ConeTypeParameterTypeImpl(lookupTag, nullability.isNullable)
is ConeFlexibleType -> { is ConeFlexibleType -> {
if (nullability == ConeNullability.UNKNOWN) { if (nullability == ConeNullability.UNKNOWN) {
if (lowerBound.nullability != upperBound.nullability || lowerBound.nullability == ConeNullability.UNKNOWN) { if (lowerBound.nullability != upperBound.nullability || lowerBound.nullability == ConeNullability.UNKNOWN) {
return this 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 ConeTypeVariableType -> ConeTypeVariableType(nullability, lookupTag)
is ConeCapturedType -> ConeCapturedType(captureStatus, lowerType, nullability, constructor) as T is ConeCapturedType -> ConeCapturedType(captureStatus, lowerType, nullability, constructor)
is ConeIntersectionType -> when (nullability) { is ConeIntersectionType -> when (nullability) {
ConeNullability.NULLABLE -> this.mapTypes { ConeNullability.NULLABLE -> this.mapTypes {
it.withNullability(nullability) it.withNullability(nullability)
} }
ConeNullability.UNKNOWN -> this // TODO: is that correct? ConeNullability.UNKNOWN -> this // TODO: is that correct?
ConeNullability.NOT_NULL -> this ConeNullability.NOT_NULL -> this
} as T }
is ConeStubType -> ConeStubType(variable, nullability) as T is ConeStubType -> ConeStubType(variable, nullability)
is ConeDefinitelyNotNullType -> when (nullability) { is ConeDefinitelyNotNullType -> when (nullability) {
ConeNullability.NOT_NULL -> this ConeNullability.NOT_NULL -> this
ConeNullability.NULLABLE -> original.withNullability(nullability) ConeNullability.NULLABLE -> original.withNullability(nullability)
ConeNullability.UNKNOWN -> original.withNullability(nullability) ConeNullability.UNKNOWN -> original.withNullability(nullability)
} as T }
is ConeIntegerLiteralType -> this is ConeIntegerLiteralType -> ConeIntegerLiteralTypeImpl(value, nullability)
else -> error("sealed: ${this::class}") else -> error("sealed: ${this::class}")
} } as T
} }
fun coneFlexibleOrSimpleType( fun coneFlexibleOrSimpleType(
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.types.model.SimpleTypeMarker
class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType { class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType {
override val possibleTypes: Collection<ConeClassLikeType> override val possibleTypes: Collection<ConeClassLikeType>
constructor(value: Long) : super(value) { constructor(value: Long, nullability: ConeNullability = ConeNullability.NOT_NULL) : super(value, nullability) {
possibleTypes = mutableListOf() possibleTypes = mutableListOf()
fun checkBoundsAndAddPossibleType(classId: ClassId, range: LongRange) { fun checkBoundsAndAddPossibleType(classId: ClassId, range: LongRange) {
@@ -34,7 +34,11 @@ class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType {
// TODO: add support of unsigned types // TODO: add support of unsigned types
} }
private constructor(value: Long, possibleTypes: Collection<ConeClassLikeType>) : super(value) { private constructor(
value: Long,
possibleTypes: Collection<ConeClassLikeType>,
nullability: ConeNullability = ConeNullability.NOT_NULL
) : super(value, nullability) {
this.possibleTypes = possibleTypes this.possibleTypes = possibleTypes
} }
@@ -46,10 +50,11 @@ class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType {
} }
override fun getApproximatedType(expectedType: ConeKotlinType?): ConeClassLikeType { override fun getApproximatedType(expectedType: ConeKotlinType?): ConeClassLikeType {
return when (expectedType) { val approximatedType = when (expectedType) {
null, !in possibleTypes -> possibleTypes.first() null, !in possibleTypes -> possibleTypes.first()
else -> expectedType as ConeClassLikeType else -> expectedType as ConeClassLikeType
} }
return approximatedType.withNullability(nullability)
} }
companion object { companion object {
@@ -121,4 +126,14 @@ fun ConeKotlinType.approximateIntegerLiteralType(expectedType: ConeKotlinType? =
(this as? ConeIntegerLiteralType)?.getApproximatedType(expectedType) ?: this (this as? ConeIntegerLiteralType)?.getApproximatedType(expectedType) ?: this
fun ConeKotlinType.approximateIntegerLiteralTypeOrNull(expectedType: ConeKotlinType? = null): ConeKotlinType? = fun ConeKotlinType.approximateIntegerLiteralTypeOrNull(expectedType: ConeKotlinType? = null): ConeKotlinType? =
(this as? ConeIntegerLiteralType)?.getApproximatedType(expectedType) (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")
}
}
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
fun box() : String { fun box() : String {
val s = "notA" val s = "notA"
val id = when (s) { val id = when (s) {
@@ -3,13 +3,13 @@ FILE fqName:<root> fileName:/whenUnusedExpression.kt
VALUE_PARAMETER name:b index:0 type:kotlin.Boolean VALUE_PARAMETER name:b index:0 type:kotlin.Boolean
VALUE_PARAMETER name:i index:1 type:kotlin.Int VALUE_PARAMETER name:i index:1 type:kotlin.Int
BLOCK_BODY BLOCK_BODY
WHEN type=kotlin.Int origin=IF WHEN type=kotlin.Int? origin=IF
BRANCH BRANCH
if: GET_VAR 'b: kotlin.Boolean declared in <root>.test' type=kotlin.Boolean origin=null if: GET_VAR 'b: kotlin.Boolean declared in <root>.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] VAR IR_TEMPORARY_VARIABLE name:tmp_0 type:kotlin.Int [val]
GET_VAR 'i: kotlin.Int declared in <root>.test' type=kotlin.Int origin=null GET_VAR 'i: kotlin.Int declared in <root>.test' type=kotlin.Int origin=null
WHEN type=kotlin.Int origin=WHEN WHEN type=kotlin.Int? origin=WHEN
BRANCH 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 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 <root>.test' type=kotlin.Int origin=null arg0: GET_VAR 'val tmp_0: kotlin.Int [val] declared in <root>.test' type=kotlin.Int origin=null