From 447c127036b9902de4f9f7f4841684a2a40be3b9 Mon Sep 17 00:00:00 2001 From: Dmitry Savvinov Date: Thu, 21 Jun 2018 12:57:36 +0300 Subject: [PATCH] Fix unsound smartcast from loop condition on assigned vars ^KT-22379 Fixed --- .../ControlStructureTypingVisitor.java | 16 ++++++++++++-- ...whileWithAssertInConditionAndBreakAfter.kt | 21 +++++++++++++++++++ ...hileWithAssertInConditionAndBreakAfter.txt | 4 ++++ ...hileWithAssertInConditionAndBreakBefore.kt | 21 +++++++++++++++++++ ...ileWithAssertInConditionAndBreakBefore.txt | 4 ++++ .../checkers/DiagnosticsTestGenerated.java | 10 +++++++++ .../DiagnosticsUsingJavacTestGenerated.java | 10 +++++++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + 8 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.txt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.kt create mode 100644 compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.txt 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 212ccd3f53d..4c8e3b43c56 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/ControlStructureTypingVisitor.java @@ -286,9 +286,16 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { dataFlowInfo = dataFlowInfo.and(loopVisitor.clearDataFlowInfoForAssignedLocalVariables(bodyTypeInfo.getJumpFlowInfo(), components.languageVersionSettings)); } + + DataFlowInfo conservativeInfoAfterLoop = + components.languageVersionSettings.supportsFeature(LanguageFeature.SoundSmartcastFromLoopConditionForLoopAssignedVariables) + ? loopVisitor.clearDataFlowInfoForAssignedLocalVariables(dataFlowInfo, components.languageVersionSettings) + : dataFlowInfo; + + return components.dataFlowAnalyzer .checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType) - .replaceDataFlowInfo(dataFlowInfo); + .replaceDataFlowInfo(conservativeInfoAfterLoop); } private boolean containsJumpOutOfLoop(@NotNull KtExpression expression, ExpressionTypingContext context) { @@ -459,9 +466,14 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor { bodyTypeInfo = loopRangeInfo; } + DataFlowInfo conservativeInfoAfterLoop = + components.languageVersionSettings.supportsFeature(LanguageFeature.SoundSmartcastFromLoopConditionForLoopAssignedVariables) + ? loopVisitor.clearDataFlowInfoForAssignedLocalVariables(loopRangeInfo.getDataFlowInfo(), components.languageVersionSettings) + : loopRangeInfo.getDataFlowInfo(); + return components.dataFlowAnalyzer .checkType(bodyTypeInfo.replaceType(components.builtIns.getUnitType()), expression, contextWithExpectedType) - .replaceDataFlowInfo(loopRangeInfo.getDataFlowInfo()); + .replaceDataFlowInfo(conservativeInfoAfterLoop); } private VariableDescriptor createLoopParameterDescriptor( diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.kt new file mode 100644 index 00000000000..0a21dea3922 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.kt @@ -0,0 +1,21 @@ +// !LANGUAGE: +SoundSmartcastFromLoopConditionForLoopAssignedVariables + +fun foo() { + var x: String? = "123" + while (x!!.length < 42) { + x = null + break + + } + x.length // 'x' is unsoundly smartcasted here +} + +fun bar() { + var x: List? = ArrayList(1) + for (i in x!!) { + x = null + break + + } + x.size // 'x' is unsoundly smartcasted here +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.txt new file mode 100644 index 00000000000..a34eb4a98fa --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.txt @@ -0,0 +1,4 @@ +package + +public fun bar(): kotlin.Unit +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.kt new file mode 100644 index 00000000000..51d3655fd56 --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.kt @@ -0,0 +1,21 @@ +// !LANGUAGE: -SoundSmartcastFromLoopConditionForLoopAssignedVariables + +fun foo() { + var x: String? = "123" + while (x!!.length < 42) { + x = null + break + + } + x.length // 'x' is unsoundly smartcasted here +} + +fun bar() { + var x: List? = ArrayList(1) + for (i in x!!) { + x = null + break + + } + x.size // 'x' is unsoundly smartcasted here +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.txt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.txt new file mode 100644 index 00000000000..a34eb4a98fa --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.txt @@ -0,0 +1,4 @@ +package + +public fun bar(): kotlin.Unit +public fun foo(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 311648915fa..c41f0f81b21 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -19822,6 +19822,16 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { public void testWhileTrueWithBreakInIfCondition() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.kt"); } + + @TestMetadata("whileWithAssertInConditionAndBreakAfter.kt") + public void testWhileWithAssertInConditionAndBreakAfter() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.kt"); + } + + @TestMetadata("whileWithAssertInConditionAndBreakBefore.kt") + public void testWhileWithAssertInConditionAndBreakBefore() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.kt"); + } } @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 2acbf4b0a13..fef89033790 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -19822,6 +19822,16 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing public void testWhileTrueWithBreakInIfCondition() throws Exception { runTest("compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.kt"); } + + @TestMetadata("whileWithAssertInConditionAndBreakAfter.kt") + public void testWhileWithAssertInConditionAndBreakAfter() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakAfter.kt"); + } + + @TestMetadata("whileWithAssertInConditionAndBreakBefore.kt") + public void testWhileWithAssertInConditionAndBreakBefore() throws Exception { + runTest("compiler/testData/diagnostics/tests/smartCasts/loops/whileWithAssertInConditionAndBreakBefore.kt"); + } } @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals") diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index c5f6770b9fc..ef59fa33de9 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -85,6 +85,7 @@ enum class LanguageFeature( NormalizeConstructorCalls(KOTLIN_1_3), StrictJavaNullabilityAssertions(KOTLIN_1_3, kind = BUG_FIX), SoundSmartcastForEnumEntries(KOTLIN_1_3, kind = BUG_FIX), + SoundSmartcastFromLoopConditionForLoopAssignedVariables(KOTLIN_1_3, kind = BUG_FIX), RestrictReturnStatementTarget(KOTLIN_1_4, kind = BUG_FIX), ProperIeee754Comparisons(sinceVersion = null, defaultState = State.DISABLED, kind = BUG_FIX),