diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java index 4b438f7ef6d..abd9ae834d4 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -96,6 +96,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); KtExpression condition = ifExpression.getCondition(); DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context); + boolean loopBreakContinuePossibleInCondition = condition != null && containsJumpOutOfLoop(condition, context); KtExpression elseBranch = ifExpression.getElse(); KtExpression thenBranch = ifExpression.getThen(); @@ -150,7 +151,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue); } - boolean loopBreakContinuePossible = thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible(); + boolean loopBreakContinuePossible = loopBreakContinuePossibleInCondition || + thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible(); boolean jumpInThen = thenType != null && KotlinBuiltIns.isNothing(thenType); boolean jumpInElse = elseType != null && KotlinBuiltIns.isNothing(elseType); @@ -170,7 +172,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { } // If break or continue was possible, take condition check info as the jump info - return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo); + return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, + loopBreakContinuePossibleInCondition ? context.dataFlowInfo : conditionDataFlowInfo); } @NotNull @@ -253,16 +256,17 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { .replaceDataFlowInfo(dataFlowInfo); } - private boolean containsJumpOutOfLoop(final KtLoopExpression loopExpression, final ExpressionTypingContext context) { + private boolean containsJumpOutOfLoop(@NotNull final KtExpression expression, final ExpressionTypingContext context) { final boolean[] result = new boolean[1]; result[0] = false; //todo breaks in inline function literals - loopExpression.accept(new KtTreeVisitor>() { + expression.accept(new KtTreeVisitor>() { @Override public Void visitBreakExpression(@NotNull KtBreakExpression breakExpression, List outerLoops) { KtSimpleNameExpression targetLabel = breakExpression.getTargetLabel(); PsiElement element = targetLabel != null ? context.trace.get(LABEL_TARGET, targetLabel) : null; - if (element == loopExpression || (targetLabel == null && outerLoops.get(outerLoops.size() - 1) == loopExpression)) { + if (outerLoops.isEmpty() || element == expression || + (targetLabel == null && outerLoops.get(outerLoops.size() - 1) == expression)) { result[0] = true; } return null; @@ -287,7 +291,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { newOuterLoops.add(loopExpression); return super.visitLoopExpression(loopExpression, newOuterLoops); } - }, Lists.newArrayList(loopExpression)); + }, expression instanceof KtLoopExpression + ? Lists.newArrayList((KtLoopExpression) expression) + : Lists.newArrayList()); return result[0]; } diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.kt new file mode 100644 index 00000000000..4dcfc4f7f3a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.kt @@ -0,0 +1,13 @@ +fun checkJump(x: Int?, y: Int?) { + while (true) { + if (x ?: break == 0) { + y!! + } else { + y!! + } + // Ok + y.hashCode() + } + // Smart cast here is erroneous: y is nullable + y.hashCode() +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.txt new file mode 100644 index 00000000000..bace7baee9a --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.txt @@ -0,0 +1,3 @@ +package + +public fun checkJump(/*0*/ x: kotlin.Int?, /*1*/ y: kotlin.Int?): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index d49b1a4d1a3..03f4139b9e1 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -16522,6 +16522,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt"); doTest(fileName); } + + @TestMetadata("WhileTrueWithBreakInIfCondition.kt") + public void testWhileTrueWithBreakInIfCondition() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals")