From 1f9d56439a72f6338620a5b90fc6d98dbfb26ae0 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 14 Nov 2017 11:44:20 +0300 Subject: [PATCH] Fix KNPE caused by optimizations in control-flow analysis The problem is that when performing full analysis we do it in a backward order while result for trivial vals is filled in a forward one. It turns out that reversedInstuctions might return a superset of forward traversed instructions, e.g. in case of dead code in lambda. At the same time result for trivial vals is constant for any instruction, thus we can just return its constant value and use it in the full analysis #KT-20895 Fixed --- .../kotlin/cfg/PseudocodeVariablesData.kt | 27 +++++++++---------- .../deadCode/returnInDeadLambda.kt | 11 ++++++++ .../deadCode/returnInDeadLambda.txt | 4 +++ .../checkers/DiagnosticsTestGenerated.java | 6 +++++ .../DiagnosticsUsingJavacTestGenerated.java | 6 +++++ 5 files changed, 40 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.kt create mode 100644 compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt index 9e28b73a1b0..10d86a9a7d9 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/PseudocodeVariablesData.kt @@ -294,8 +294,14 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon val variableUseStatusData: Map> get() { - val resultForTrivialVals = computeUseInfoForTrivialVals() - if (rootVariables.nonTrivialVariables.isEmpty()) return resultForTrivialVals + val edgesForTrivialVals = computeUseInfoForTrivialVals() + if (rootVariables.nonTrivialVariables.isEmpty()) { + return hashMapOf>().apply { + pseudocode.traverse(TraversalOrder.FORWARD) { instruction -> + put(instruction, edgesForTrivialVals) + } + } + } return pseudocodeVariableDataCollector.collectData(TraversalOrder.BACKWARD, UseControlFlowInfo()) { instruction: Instruction, incomingEdgesData: Collection -> @@ -336,17 +342,16 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon Edges(enterResult, exitResult) } }.mapValues { - (instruction, edges) -> - val edgeForTrivialVals = resultForTrivialVals[instruction]!! + (_, edges) -> Edges( - edgeForTrivialVals.incoming.replaceDelegate(edges.incoming), - edgeForTrivialVals.outgoing.replaceDelegate(edges.outgoing) + edgesForTrivialVals.incoming.replaceDelegate(edges.incoming), + edgesForTrivialVals.outgoing.replaceDelegate(edges.outgoing) ) } } - private fun computeUseInfoForTrivialVals(): Map> { + private fun computeUseInfoForTrivialVals(): Edges { val used = hashSetOf() pseudocode.traverse(TraversalOrder.FORWARD) { instruction -> @@ -358,13 +363,7 @@ class PseudocodeVariablesData(val pseudocode: Pseudocode, private val bindingCon } val constantUseInfo = ReadOnlyUseControlFlowInfoImpl(used, null) - val constantEdges = Edges(constantUseInfo, constantUseInfo) - val result = hashMapOf>() - - pseudocode.traverse(TraversalOrder.FORWARD) { instruction -> - result[instruction] = constantEdges - } - return result + return Edges(constantUseInfo, constantUseInfo) } private fun extractValWithTrivialInitializer(instruction: Instruction): VariableDescriptor? { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.kt new file mode 100644 index 00000000000..11fbbcc02b1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.kt @@ -0,0 +1,11 @@ + +inline fun myRun(b: () -> Unit) = b() + +fun foo() { + var a: Int + return + + myRun { + return + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.txt new file mode 100644 index 00000000000..a55dbd56cad --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.txt @@ -0,0 +1,4 @@ +package + +public fun foo(): kotlin.Unit +public inline fun myRun(/*0*/ b: () -> kotlin.Unit): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 3ed8e455d86..e0855333057 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -4397,6 +4397,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.kt"); doTest(fileName); } + + @TestMetadata("returnInDeadLambda.kt") + public void testReturnInDeadLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn") diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 73664e77e53..a1d041b2af2 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -4397,6 +4397,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/kt5200DeadCodeInLambdas.kt"); doTest(fileName); } + + @TestMetadata("returnInDeadLambda.kt") + public void testReturnInDeadLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/returnInDeadLambda.kt"); + doTest(fileName); + } } @TestMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/definiteReturn")