FIR DFA: isolate effects between blocks in try expression

This commit is contained in:
Jinseong Jeon
2020-10-26 22:54:46 -07:00
committed by Dmitriy Novozhilov
parent 1f1e1828a7
commit bd173ebebc
9 changed files with 24 additions and 21 deletions
@@ -637,7 +637,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
fun enterTryExpression(tryExpression: FirTryExpression) { fun enterTryExpression(tryExpression: FirTryExpression) {
val (tryExpressionEnterNode, tryMainBlockEnterNode) = graphBuilder.enterTryExpression(tryExpression) val (tryExpressionEnterNode, tryMainBlockEnterNode) = graphBuilder.enterTryExpression(tryExpression)
tryExpressionEnterNode.mergeIncomingFlow() tryExpressionEnterNode.mergeIncomingFlow()
tryMainBlockEnterNode.mergeIncomingFlow() // NB: fork to isolate effects inside the try main block
// Otherwise, changes in the try main block could affect the try expression enter node as well as its previous nodes.
tryMainBlockEnterNode.mergeIncomingFlow(shouldForkFlow = true)
} }
fun exitTryMainBlock(tryExpression: FirTryExpression) { fun exitTryMainBlock(tryExpression: FirTryExpression) {
@@ -645,7 +647,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
fun enterCatchClause(catch: FirCatch) { fun enterCatchClause(catch: FirCatch) {
graphBuilder.enterCatchClause(catch).mergeIncomingFlow(updateReceivers = true) // NB: fork to isolate effects inside the catch clause
// Otherwise, changes in the catch clause could affect the previous node: try main block.
graphBuilder.enterCatchClause(catch).mergeIncomingFlow(updateReceivers = true, shouldForkFlow = true)
} }
fun exitCatchClause(catch: FirCatch) { fun exitCatchClause(catch: FirCatch) {
@@ -653,19 +657,20 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
fun enterFinallyBlock() { fun enterFinallyBlock() {
// TODO // NB: fork to isolate effects inside the finally block
graphBuilder.enterFinallyBlock().mergeIncomingFlow() // Otherwise, changes in the finally block could affect the previous nodes: try main block and catch clauses.
graphBuilder.enterFinallyBlock().mergeIncomingFlow(shouldForkFlow = true)
} }
fun exitFinallyBlock(tryExpression: FirTryExpression) { fun exitFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow() graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow()
} }
fun exitTryExpression(callCompleted: Boolean) { fun exitTryExpression(callCompleted: Boolean) {
// TODO
val (tryExpressionExitNode, unionNode) = graphBuilder.exitTryExpression(callCompleted) val (tryExpressionExitNode, unionNode) = graphBuilder.exitTryExpression(callCompleted)
tryExpressionExitNode.mergeIncomingFlow() // NB: fork to prevent effects after the try expression from being flown into the try expression
// Otherwise, changes in any following nodes could affect the previous nodes, including try main block and finally block if any.
tryExpressionExitNode.mergeIncomingFlow(shouldForkFlow = true)
unionNode?.let { unionFlowFromArguments(it) } unionNode?.let { unionFlowFromArguments(it) }
} }
-2
View File
@@ -1,5 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
sealed class Result { sealed class Result {
class Failure(val exception: Exception) : Result() class Failure(val exception: Exception) : Result()
class Success(val message: String) : Result() class Success(val message: String) : Result()
@@ -14,7 +14,7 @@ fun tryCatchFinally(x: Int?) {
} catch (e: Exception) { } catch (e: Exception) {
x!! x!!
} finally { } finally {
checkSubtype<Int>(x) <!INAPPLICABLE_CANDIDATE!>checkSubtype<!><Int>(x)
x!! x!!
} }
checkSubtype<Int>(x) checkSubtype<Int>(x)
@@ -2,11 +2,11 @@ fun castInTry(s: Any) {
try { try {
s as String // Potential cast exception s as String // Potential cast exception
} catch (e: Exception) { } catch (e: Exception) {
s.length // shouldn't be resolved s.<!UNRESOLVED_REFERENCE!>length<!> // shouldn't be resolved
} finally { } finally {
s.length // shouldn't be resolved s.<!UNRESOLVED_REFERENCE!>length<!> // shouldn't be resolved
} }
s.length // shouldn't be resolved s.<!UNRESOLVED_REFERENCE!>length<!> // shouldn't be resolved
} }
fun castInTryAndCatch(s: Any) { fun castInTryAndCatch(s: Any) {
@@ -15,9 +15,9 @@ fun castInTryAndCatch(s: Any) {
} catch (e: Exception) { } catch (e: Exception) {
s as String // Potential cast exception s as String // Potential cast exception
} finally { } finally {
s.length // shouldn't be resolved s.<!UNRESOLVED_REFERENCE!>length<!> // shouldn't be resolved
} }
s.length // should be smartcast s.<!UNRESOLVED_REFERENCE!>length<!> // should be smartcast
} }
fun castAtAll(s: Any) { fun castAtAll(s: Any) {
@@ -2,9 +2,9 @@ fun castInTry(s: Any) {
try { try {
s as String // Potential cast exception s as String // Potential cast exception
} finally { } finally {
s.length // Shouldn't be resolved s.<!UNRESOLVED_REFERENCE!>length<!> // Shouldn't be resolved
} }
s.length // Shouldn't be resolved s.<!UNRESOLVED_REFERENCE!>length<!> // Shouldn't be resolved
} }
fun castInTryAndFinally(s: Any) { fun castInTryAndFinally(s: Any) {
@@ -37,7 +37,7 @@ fun test3() {
catch (e: B) { catch (e: B) {
return return
} }
a.hashCode() // a is nullable here a.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() // a is nullable here
} }
fun test4() { fun test4() {
var a: Int? = null var a: Int? = null
@@ -10,5 +10,5 @@ fun foo() {
} catch (ex: Exception) {} } catch (ex: Exception) {}
bar(s) bar(s)
if (s != null) { } if (s != null) { }
s.hashCode() s.<!INAPPLICABLE_CANDIDATE!>hashCode<!>()
} }
@@ -17,5 +17,5 @@ fun testWithCatch(x: Any?) {
x.length x.length
} catch (e: java.lang.IllegalArgumentException) { } } catch (e: java.lang.IllegalArgumentException) { }
x.length x.<!UNRESOLVED_REFERENCE!>length<!>
} }
@@ -45,7 +45,7 @@ fun testTryCatch(x: Any?) {
} catch (e: kotlin.IllegalArgumentException) { } catch (e: kotlin.IllegalArgumentException) {
} }
x.length x.<!UNRESOLVED_REFERENCE!>length<!>
} }
fun testUncertainFlow(x: Any?) { fun testUncertainFlow(x: Any?) {