psi2ir: keep the type of when when possible

Ideally, the type of `IrWhen` should be provided by type inference for
a consistent behavior. `USED_AS_EXPRESSION` from CFG isn't always
consistent with type inference, unfortunately.

The behavior is now aligned with `if`. The type of `when` is kept when
it *can* be an expression, instead of whether it is used or not.
This commit is contained in:
Ting-Yuan Huang
2019-05-23 10:28:09 -07:00
committed by max-kammerer
parent cd651be461
commit 1b4d26e490
7 changed files with 69 additions and 8 deletions
@@ -1267,6 +1267,11 @@ public class Fir2IrTextTestGenerated extends AbstractFir2IrTextTest {
runTest("compiler/testData/ir/irText/expressions/whenReturn.kt");
}
@TestMetadata("whenUnusedExpression.kt")
public void testWhenUnusedExpression() throws Exception {
runTest("compiler/testData/ir/irText/expressions/whenUnusedExpression.kt");
}
@TestMetadata("whenWithSubjectVariable.kt")
public void testWhenWithSubjectVariable() throws Exception {
runTest("compiler/testData/ir/irText/expressions/whenWithSubjectVariable.kt");
@@ -104,11 +104,9 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
val inferredType = getInferredTypeWithImplicitCastsOrFail(expression)
// TODO relies on ControlFlowInformationProvider, get rid of it
val isUsedAsExpression = get(BindingContext.USED_AS_EXPRESSION, expression) ?: false
val resultType = when {
isUsedAsExpression -> inferredType.toIrType()
// Non-exhaustive when can only be used as statement.
expression.isExhaustiveWhen() -> inferredType.toIrType()
KotlinBuiltIns.isNothing(inferredType) -> inferredType.toIrType()
else -> context.irBuiltIns.unitType
}
@@ -1,7 +1,3 @@
// Even before any IR lowerings, the type of `when` is determined to be
// Unit even though the outer `if` still returns `Int?`. This results
// in a ClassCastException when that Unit is converted into a Number.
// IGNORE_BACKEND: JVM_IR
fun test(
b: Boolean,
i: Int
@@ -0,0 +1,22 @@
FILE fqName:<root> fileName:/whenUnusedExpression.kt
FUN name:test visibility:public modality:FINAL <> (b:kotlin.Boolean, i:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER name:b index:0 type:kotlin.Boolean
VALUE_PARAMETER name:i index:1 type:kotlin.Int
BLOCK_BODY
WHEN type=kotlin.Unit origin=IF
BRANCH
if: GET_VAR 'b: kotlin.Boolean declared in <root>.test' type=kotlin.Boolean origin=null
then: BLOCK type=kotlin.Unit origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Int [val]
WHEN type=kotlin.Unit 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 tmp0_subject: kotlin.Int [val] declared in <root>.test' type=kotlin.Int origin=null
arg1: CONST Int type=kotlin.Unit value=0
then: CONST Int type=kotlin.Unit value=1
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Null type=kotlin.Nothing? value=null
@@ -0,0 +1,10 @@
// WITH_RUNTIME
fun test(b: Boolean, i: Int) {
if (b) {
when (i) {
0 -> 1
else -> null
}
} else null
}
@@ -0,0 +1,25 @@
FILE fqName:<root> fileName:/whenUnusedExpression.kt
FUN name:test visibility:public modality:FINAL <> (b:kotlin.Boolean, i:kotlin.Int) returnType:kotlin.Unit
VALUE_PARAMETER name:b index:0 type:kotlin.Boolean
VALUE_PARAMETER name:i index:1 type:kotlin.Int
BLOCK_BODY
TYPE_OP type=kotlin.Unit origin=IMPLICIT_COERCION_TO_UNIT typeOperand=kotlin.Unit
WHEN type=kotlin.Int? origin=IF
BRANCH
if: GET_VAR 'b: kotlin.Boolean declared in <root>.test' type=kotlin.Boolean origin=null
then: BLOCK type=kotlin.Int? origin=null
BLOCK type=kotlin.Int? origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Int [val]
GET_VAR 'i: kotlin.Int declared in <root>.test' type=kotlin.Int origin=null
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 tmp0_subject: kotlin.Int [val] declared in <root>.test' type=kotlin.Int origin=null
arg1: CONST Int type=kotlin.Int value=0
then: CONST Int type=kotlin.Int value=1
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Null type=kotlin.Nothing? value=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Null type=kotlin.Nothing? value=null
@@ -1267,6 +1267,11 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
runTest("compiler/testData/ir/irText/expressions/whenReturn.kt");
}
@TestMetadata("whenUnusedExpression.kt")
public void testWhenUnusedExpression() throws Exception {
runTest("compiler/testData/ir/irText/expressions/whenUnusedExpression.kt");
}
@TestMetadata("whenWithSubjectVariable.kt")
public void testWhenWithSubjectVariable() throws Exception {
runTest("compiler/testData/ir/irText/expressions/whenWithSubjectVariable.kt");