FIR CFA: make node unionness a runtime property

Class exit node should only unify data flow if it is an anonymous
object, in which case it only has one control flow input. Coversely,
any other class exit node has normal merge semantics for its control
flow inputs, but it won't have any data flow inputs.
This commit is contained in:
pyos
2023-01-18 13:21:14 +01:00
committed by teamcity
parent 9f17b5de97
commit 166ddd9ea9
14 changed files with 59 additions and 73 deletions
@@ -8,6 +8,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.*
@@ -31,6 +32,12 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
open val canThrow: Boolean get() = false
// a ---> b ---> d
// \-> c -/
// Normal CFG semantics: a, then either b or c, then d
// If d is a union node: a, then *both* b and c in some unknown order, then d
open val isUnion: Boolean get() = false
companion object {
@CfgInternals
fun addEdge(
@@ -107,7 +114,7 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
@CfgInternals
fun updateDeadStatus() {
isDead = if (this is UnionNodeMarker)
isDead = if (isUnion)
_incomingEdges?.let { map -> map.values.any { it.kind.isDead } } == true
else
_incomingEdges?.let { map -> map.size == previousNodes.size && map.values.all { it.kind.isDead || !it.kind.usedInCfa } } == true
@@ -128,12 +135,6 @@ interface ExitNodeMarker
interface GraphEnterNodeMarker : EnterNodeMarker
interface GraphExitNodeMarker : ExitNodeMarker
// a ---> b ---> d
// \-> c -/
// Normal CFG semantics: a, then either b or c, then d
// If d is an instance of this interface: a, then *both* b and c in some unknown order, then d
interface UnionNodeMarker
// ----------------------------------- EnterNode for declaration with CFG -----------------------------------
sealed class CFGNodeWithSubgraphs<out E : FirElement>(owner: ControlFlowGraph, level: Int) : CFGNode<E>(owner, level) {
@@ -245,7 +246,10 @@ class ClassEnterNode(owner: ControlFlowGraph, override val fir: FirClass, level:
}
class ClassExitNode(owner: ControlFlowGraph, override val fir: FirClass, level: Int) : CFGNodeWithSubgraphs<FirClass>(owner, level),
GraphExitNodeMarker, UnionNodeMarker {
GraphExitNodeMarker {
override val isUnion: Boolean
get() = fir is FirAnonymousObject && fir.classKind != ClassKind.ENUM_ENTRY
@set:CfgInternals
override lateinit var subGraphs: List<ControlFlowGraph>
@@ -305,7 +309,10 @@ class PropertyInitializerExitNode(owner: ControlFlowGraph, override val fir: Fir
class DelegateExpressionExitNode(owner: ControlFlowGraph, override val fir: FirExpression, level: Int)
: CFGNode<FirExpression>(owner, level), UnionNodeMarker {
: CFGNode<FirExpression>(owner, level) {
override val isUnion: Boolean get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitDelegateExpressionExitNode(this, data)
}
@@ -610,10 +617,13 @@ class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExp
// ----------------------------------- Check not null call -----------------------------------
class CheckNotNullCallNode(owner: ControlFlowGraph, override val fir: FirCheckNotNullCall, level: Int)
: CFGNode<FirCheckNotNullCall>(owner, level), UnionNodeMarker {
: CFGNode<FirCheckNotNullCall>(owner, level) {
override val canThrow: Boolean
get() = true
override val isUnion: Boolean
get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitCheckNotNullCallNode(this, data)
}
@@ -645,10 +655,14 @@ class ResolvedQualifierNode(
}
class FunctionCallNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int)
: CFGNode<FirFunctionCall>(owner, level), UnionNodeMarker {
: CFGNode<FirFunctionCall>(owner, level) {
override val canThrow: Boolean
get() = true
override val isUnion: Boolean
get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitFunctionCallNode(this, data)
}
@@ -671,7 +685,11 @@ class GetClassCallNode(owner: ControlFlowGraph, override val fir: FirGetClassCal
}
class DelegatedConstructorCallNode(owner: ControlFlowGraph, override val fir: FirDelegatedConstructorCall, level: Int)
: CFGNode<FirDelegatedConstructorCall>(owner, level), UnionNodeMarker {
: CFGNode<FirDelegatedConstructorCall>(owner, level) {
override val isUnion: Boolean
get() = true
override val canThrow: Boolean
get() = true // shouldn't matter since delegated constructor calls aren't wrapped in try-finally, but still
@@ -681,7 +699,11 @@ class DelegatedConstructorCallNode(owner: ControlFlowGraph, override val fir: Fi
}
class StringConcatenationCallNode(owner: ControlFlowGraph, override val fir: FirStringConcatenationCall, level: Int)
: CFGNode<FirStringConcatenationCall>(owner, level), UnionNodeMarker {
: CFGNode<FirStringConcatenationCall>(owner, level) {
override val isUnion: Boolean
get() = true
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitStringConcatenationCallNode(this, data)
}
@@ -804,4 +826,4 @@ class SmartCastExpressionExitNode(owner: ControlFlowGraph, override val fir: Fir
override fun <R, D> accept(visitor: ControlFlowGraphVisitor<R, D>, data: D): R {
return visitor.visitSmartCastExpressionExitNode(this, data)
}
}
}
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg
abstract class ControlFlowGraphVisitor<out R, in D> {
abstract fun visitNode(node: CFGNode<*>, data: D): R
abstract fun <T> visitUnionNode(node: T, data: D): R where T : CFGNode<*>, T : UnionNodeMarker
// ----------------------------------- Simple function -----------------------------------
open fun visitFunctionEnterNode(node: FunctionEnterNode, data: D): R {
@@ -103,7 +101,7 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
}
open fun visitDelegateExpressionExitNode(node: DelegateExpressionExitNode, data: D): R {
return visitUnionNode(node, data)
return visitNode(node, data)
}
// ----------------------------------- Field -----------------------------------
@@ -296,7 +294,7 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
// ----------------------------------- Check not null call -----------------------------------
open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode, data: D): R {
return visitUnionNode(node, data)
return visitNode(node, data)
}
// ----------------------------------- Resolvable call -----------------------------------
@@ -310,7 +308,7 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
}
open fun visitFunctionCallNode(node: FunctionCallNode, data: D): R {
return visitUnionNode(node, data)
return visitNode(node, data)
}
open fun visitCallableReferenceNode(node: CallableReferenceNode, data: D): R {
@@ -322,11 +320,11 @@ abstract class ControlFlowGraphVisitor<out R, in D> {
}
open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode, data: D): R {
return visitUnionNode(node, data)
return visitNode(node, data)
}
open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode, data: D): R {
return visitUnionNode(node, data)
return visitNode(node, data)
}
open fun visitThrowExceptionNode(node: ThrowExceptionNode, data: D): R {
@@ -8,8 +8,6 @@ package org.jetbrains.kotlin.fir.resolve.dfa.cfg
abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothing?>() {
abstract fun visitNode(node: CFGNode<*>)
abstract fun <T> visitUnionNode(node: T) where T : CFGNode<*>, T : UnionNodeMarker
// ----------------------------------- Simple function -----------------------------------
open fun visitFunctionEnterNode(node: FunctionEnterNode) {
@@ -53,7 +51,7 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
}
open fun visitDelegateExpressionExitNode(node: DelegateExpressionExitNode) {
visitUnionNode(node)
visitNode(node)
}
// ----------------------------------- Init -----------------------------------
@@ -236,7 +234,7 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
// ----------------------------------- Check not null call -----------------------------------
open fun visitCheckNotNullCallNode(node: CheckNotNullCallNode) {
visitUnionNode(node)
visitNode(node)
}
// ----------------------------------- Resolvable call -----------------------------------
@@ -250,15 +248,15 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
}
open fun visitFunctionCallNode(node: FunctionCallNode) {
visitUnionNode(node)
visitNode(node)
}
open fun visitDelegatedConstructorCallNode(node: DelegatedConstructorCallNode) {
visitUnionNode(node)
visitNode(node)
}
open fun visitStringConcatenationCallNode(node: StringConcatenationCallNode) {
visitUnionNode(node)
visitNode(node)
}
open fun visitThrowExceptionNode(node: ThrowExceptionNode) {
@@ -291,10 +289,6 @@ abstract class ControlFlowGraphVisitorVoid : ControlFlowGraphVisitor<Unit, Nothi
visitNode(node)
}
final override fun <T> visitUnionNode(node: T, data: Nothing?) where T : CFGNode<*>, T : UnionNodeMarker {
visitUnionNode(node)
}
// ----------------------------------- Simple function -----------------------------------
final override fun visitFunctionEnterNode(node: FunctionEnterNode, data: Nothing?) {