From 21dfee456522140fea52a90edf89e0d6a4331406 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Thu, 3 May 2018 16:25:05 +0300 Subject: [PATCH] psi2ir: inferred type for 'when' is Nothing only if it's exhaustive TODO: fix inference bug --- .../BranchingExpressionGenerator.kt | 23 ++++--- .../whenByFloatingPoint.txt | 2 +- .../testData/ir/irText/expressions/when.txt | 2 +- .../testData/ir/irText/regressions/kt24114.kt | 27 ++++++++ .../ir/irText/regressions/kt24114.txt | 68 +++++++++++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 5 ++ 6 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 compiler/testData/ir/irText/regressions/kt24114.kt create mode 100644 compiler/testData/ir/irText/regressions/kt24114.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt index b6f90f0cfa8..c090b4687f4 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/BranchingExpressionGenerator.kt @@ -88,15 +88,15 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta scope.createTemporaryVariable(it.genExpr(), "subject") } - val inferredType = getInferredTypeWithImplicitCastsOrFail(expression) // TODO relies on ControlFlowInformationProvider, get rid of it - val isUsedAsExpression = context.bindingContext[BindingContext.USED_AS_EXPRESSION, expression] ?: false + val isUsedAsExpression = get(BindingContext.USED_AS_EXPRESSION, expression) ?: false + val isExhaustive = expression.isExhaustiveWhen() val resultType = when { isUsedAsExpression -> inferredType - KotlinBuiltIns.isNothing(inferredType) -> inferredType + isExhaustive && KotlinBuiltIns.isNothing(inferredType) -> inferredType else -> context.builtIns.unitType } @@ -129,10 +129,8 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta private fun addElseBranchForExhaustiveWhenIfNeeded(irWhen: IrWhen, whenExpression: KtWhenExpression) { if (irWhen.branches.filterIsInstance().isEmpty()) { - val bindingContext = context.bindingContext //TODO: check condition: seems it's safe to always generate exception - val isExhaustive = java.lang.Boolean.TRUE == bindingContext.get(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, whenExpression) || - java.lang.Boolean.TRUE == bindingContext.get(BindingContext.EXHAUSTIVE_WHEN, whenExpression) + val isExhaustive = whenExpression.isExhaustiveWhen() if (isExhaustive) { val call = IrCallImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.noWhenBranchMatchedExceptionSymbol) @@ -141,6 +139,11 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta } } + private fun KtWhenExpression.isExhaustiveWhen(): Boolean = + elseExpression != null // TODO front-end should provide correct exhaustiveness information + || true == get(BindingContext.EXHAUSTIVE_WHEN, this) + || true == get(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, this) + private fun generateWhenBody(expression: KtWhenExpression, irSubject: IrVariable?, irWhen: IrWhen): IrExpression { return if (irSubject == null) { if (irWhen.branches.isEmpty()) @@ -178,10 +181,14 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta } private fun generateIsPatternCondition(irSubject: IrVariable, ktCondition: KtWhenConditionIsPattern): IrExpression { - val isType = getOrFail(BindingContext.TYPE, ktCondition.typeReference) + val typeOperand = getOrFail(BindingContext.TYPE, ktCondition.typeReference) + val typeOperandDescriptor = typeOperand.constructor.declarationDescriptor + ?: throw AssertionError("No declaration descriptor for type $typeOperand") + val typeOperandSymbol = context.symbolTable.referenceClassifier(typeOperandDescriptor) + return IrTypeOperatorCallImpl( ktCondition.startOffset, ktCondition.endOffset, context.builtIns.booleanType, - IrTypeOperator.INSTANCEOF, isType, irSubject.defaultLoad() + IrTypeOperator.INSTANCEOF, typeOperand, irSubject.defaultLoad(), typeOperandSymbol ) } diff --git a/compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.txt b/compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.txt index b92fad1a13b..702603a53ec 100644 --- a/compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.txt +++ b/compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.txt @@ -76,7 +76,7 @@ FILE fqName: fileName:/whenByFloatingPoint.kt WHEN type=kotlin.Int origin=WHEN BRANCH if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Double - typeOperand: UNBOUND: class Double : kotlin.Number, kotlin.Comparable, java.io.Serializable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Double modality:FINAL visibility:public flags: GET_VAR 'tmp0_subject: Any' type=kotlin.Any origin=null then: CONST Int type=kotlin.Int value=-1 BRANCH diff --git a/compiler/testData/ir/irText/expressions/when.txt b/compiler/testData/ir/irText/expressions/when.txt index be8b1b07134..252ec385d8c 100644 --- a/compiler/testData/ir/irText/expressions/when.txt +++ b/compiler/testData/ir/irText/expressions/when.txt @@ -40,7 +40,7 @@ FILE fqName: fileName:/when.kt then: CONST String type=kotlin.String value=A BRANCH if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.String - typeOperand: UNBOUND: class String : kotlin.Comparable, kotlin.CharSequence, java.io.Serializable + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:String modality:FINAL visibility:public flags: GET_VAR 'tmp0_subject: Any?' type=kotlin.Any? origin=null then: CONST String type=kotlin.String value=String BRANCH diff --git a/compiler/testData/ir/irText/regressions/kt24114.kt b/compiler/testData/ir/irText/regressions/kt24114.kt new file mode 100644 index 00000000000..9a9c944d70f --- /dev/null +++ b/compiler/testData/ir/irText/regressions/kt24114.kt @@ -0,0 +1,27 @@ +fun one() = 1 +fun two() = 2 + +fun test1(): Int { + while (true) { + when (one()) { + 1 -> { + when(two()) { + 2 -> return 2 + } + } // : Nothing + else -> return 3 + } + } +} + +fun test2(): Int { + while (true) { + when (one()) { + 1 -> + when (two()) { + 2 -> return 2 + } // : Unit -> Nothing + else -> return 3 + } // : Nothing + } +} diff --git a/compiler/testData/ir/irText/regressions/kt24114.txt b/compiler/testData/ir/irText/regressions/kt24114.txt new file mode 100644 index 00000000000..4ec01e3fb61 --- /dev/null +++ b/compiler/testData/ir/irText/regressions/kt24114.txt @@ -0,0 +1,68 @@ +FILE fqName: fileName:/kt24114.kt + FUN name:one visibility:public modality:FINAL <> () returnType:Int flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='one(): Int' + CONST Int type=kotlin.Int value=1 + FUN name:two visibility:public modality:FINAL <> () returnType:Int flags: + BLOCK_BODY + RETURN type=kotlin.Nothing from='two(): Int' + CONST Int type=kotlin.Int value=2 + FUN name:test1 visibility:public modality:FINAL <> () returnType:Int flags: + BLOCK_BODY + WHILE label=null origin=WHILE_LOOP + condition: CONST Boolean type=kotlin.Boolean value=true + body: BLOCK type=kotlin.Unit origin=null + BLOCK type=kotlin.Nothing origin=WHEN + VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Int flags:val + CALL 'one(): Int' type=kotlin.Int origin=null + WHEN type=kotlin.Nothing origin=WHEN + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp0_subject: Int' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=1 + then: BLOCK type=kotlin.Nothing origin=null + TYPE_OP type=kotlin.Nothing origin=IMPLICIT_CAST typeOperand=kotlin.Nothing + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Nothing modality:FINAL visibility:public flags: + BLOCK type=kotlin.Unit origin=WHEN + VAR IR_TEMPORARY_VARIABLE name:tmp1_subject type:kotlin.Int flags:val + CALL 'two(): Int' type=kotlin.Int origin=null + WHEN type=kotlin.Unit origin=WHEN + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp1_subject: Int' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=2 + then: RETURN type=kotlin.Nothing from='test1(): Int' + CONST Int type=kotlin.Int value=2 + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: RETURN type=kotlin.Nothing from='test1(): Int' + CONST Int type=kotlin.Int value=3 + FUN name:test2 visibility:public modality:FINAL <> () returnType:Int flags: + BLOCK_BODY + WHILE label=null origin=WHILE_LOOP + condition: CONST Boolean type=kotlin.Boolean value=true + body: BLOCK type=kotlin.Unit origin=null + BLOCK type=kotlin.Nothing origin=WHEN + VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Int flags:val + CALL 'one(): Int' type=kotlin.Int origin=null + WHEN type=kotlin.Nothing origin=WHEN + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp0_subject: Int' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=1 + then: TYPE_OP type=kotlin.Nothing origin=IMPLICIT_CAST typeOperand=kotlin.Nothing + typeOperand: CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Nothing modality:FINAL visibility:public flags: + BLOCK type=kotlin.Unit origin=WHEN + VAR IR_TEMPORARY_VARIABLE name:tmp1_subject type:kotlin.Int flags:val + CALL 'two(): Int' type=kotlin.Int origin=null + WHEN type=kotlin.Unit origin=WHEN + BRANCH + if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ + arg0: GET_VAR 'tmp1_subject: Int' type=kotlin.Int origin=null + arg1: CONST Int type=kotlin.Int value=2 + then: RETURN type=kotlin.Nothing from='test2(): Int' + CONST Int type=kotlin.Int value=2 + BRANCH + if: CONST Boolean type=kotlin.Boolean value=true + then: RETURN type=kotlin.Nothing from='test2(): Int' + CONST Int type=kotlin.Int value=3 diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 4cf97526293..068c7c71de7 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -1256,6 +1256,11 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { runTest("compiler/testData/ir/irText/regressions/integerCoercionToT.kt"); } + @TestMetadata("kt24114.kt") + public void testKt24114() throws Exception { + runTest("compiler/testData/ir/irText/regressions/kt24114.kt"); + } + @TestMetadata("typeAliasCtorForGenericClass.kt") public void testTypeAliasCtorForGenericClass() throws Exception { runTest("compiler/testData/ir/irText/regressions/typeAliasCtorForGenericClass.kt");