FIR DFA: update assignment indices when joining flows
This commit is contained in:
+1
-2
@@ -1221,7 +1221,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
// TODO: add unstable smartcast for non-local var
|
// TODO: add unstable smartcast for non-local var
|
||||||
val variable = variableStorage.getRealVariableWithoutUnwrappingAlias(node.flow, property.symbol, assignment)
|
val variable = variableStorage.getRealVariableWithoutUnwrappingAlias(node.flow, property.symbol, assignment)
|
||||||
if (variable != null) {
|
if (variable != null) {
|
||||||
logicSystem.removeAllAboutVariable(node.flow, variable)
|
logicSystem.recordNewAssignment(node.flow, variable, context.newAssignmentIndex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
processConditionalContract(assignment)
|
processConditionalContract(assignment)
|
||||||
@@ -1243,7 +1243,6 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
)
|
)
|
||||||
val isAssignment = assignment != null
|
val isAssignment = assignment != null
|
||||||
if (isAssignment) {
|
if (isAssignment) {
|
||||||
logicSystem.removeAllAboutVariable(flow, propertyVariable)
|
|
||||||
logicSystem.recordNewAssignment(flow, propertyVariable, context.newAssignmentIndex())
|
logicSystem.recordNewAssignment(flow, propertyVariable, context.newAssignmentIndex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-2
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.fir.types.ConeInferenceContext
|
|||||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
data class PersistentTypeStatement(
|
data class PersistentTypeStatement(
|
||||||
override val variable: RealVariable,
|
override val variable: RealVariable,
|
||||||
@@ -138,13 +139,20 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
|||||||
// the aliasing of `y` to `a.x` after `if (p) { y = a.x } else { y = a.x }`.
|
// the aliasing of `y` to `a.x` after `if (p) { y = a.x } else { y = a.x }`.
|
||||||
val commonAliases = computeCommonAliases(flows)
|
val commonAliases = computeCommonAliases(flows)
|
||||||
|
|
||||||
|
// If a variable was reassigned in one branch, it was reassigned at the join point.
|
||||||
|
val reassignedVariables = mutableMapOf<RealVariable, Int>()
|
||||||
for (flow in flows) {
|
for (flow in flows) {
|
||||||
for ((variable, index) in flow.assignmentIndex) {
|
for ((variable, index) in flow.assignmentIndex) {
|
||||||
if (commonFlow.assignmentIndex[variable] != index) {
|
if (commonFlow.assignmentIndex[variable] != index) {
|
||||||
removeAllAboutVariable(commonFlow, variable)
|
// Ideally we should generate an entirely new index here, but it doesn't really
|
||||||
|
// matter; the important part is that it's different from `commonFlow.previousFlow`.
|
||||||
|
reassignedVariables[variable] = max(index, reassignedVariables[variable] ?: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for ((variable, index) in reassignedVariables) {
|
||||||
|
recordNewAssignment(commonFlow, variable, index)
|
||||||
|
}
|
||||||
|
|
||||||
val toReplace = statements.map { it.variable to it.toPersistent() }
|
val toReplace = statements.map { it.variable to it.toPersistent() }
|
||||||
commonFlow.approvedTypeStatements += toReplace
|
commonFlow.approvedTypeStatements += toReplace
|
||||||
@@ -158,7 +166,7 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
|||||||
return commonFlow
|
return commonFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computeCommonAliases(flows: Collection<PersistentFlow>): MutableMap<RealVariable, RealVariableAndType> =
|
private fun computeCommonAliases(flows: Collection<PersistentFlow>): Map<RealVariable, RealVariableAndType> =
|
||||||
flows.first().directAliasMap.filterTo(mutableMapOf()) { (variable, alias) ->
|
flows.first().directAliasMap.filterTo(mutableMapOf()) { (variable, alias) ->
|
||||||
flows.all { it.directAliasMap[variable] == alias }
|
flows.all { it.directAliasMap[variable] == alias }
|
||||||
}
|
}
|
||||||
@@ -471,6 +479,7 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun recordNewAssignment(flow: PersistentFlow, variable: RealVariable, index: Int) {
|
override fun recordNewAssignment(flow: PersistentFlow, variable: RealVariable, index: Int) {
|
||||||
|
removeAllAboutVariable(flow, variable)
|
||||||
flow.assignmentIndex = flow.assignmentIndex.put(variable, index)
|
flow.assignmentIndex = flow.assignmentIndex.put(variable, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
@@ -38,3 +38,26 @@ fun test3(p: Boolean) {
|
|||||||
x.length // ok
|
x.length // ok
|
||||||
c.x<!UNSAFE_CALL!>.<!>length // bad
|
c.x<!UNSAFE_CALL!>.<!>length // bad
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun test4(p: Boolean, q: Boolean) {
|
||||||
|
var c = C("...")
|
||||||
|
val x = c.x
|
||||||
|
if (x == null) return
|
||||||
|
x.length // ok
|
||||||
|
c.x.length // ok
|
||||||
|
if (p) {
|
||||||
|
if (q) {
|
||||||
|
c = C(null)
|
||||||
|
} else {
|
||||||
|
c = C(null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (q) {
|
||||||
|
c = C(null)
|
||||||
|
} else {
|
||||||
|
c = C(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x.length // ok
|
||||||
|
c.x<!UNSAFE_CALL!>.<!>length // bad
|
||||||
|
}
|
||||||
|
|||||||
+23
@@ -38,3 +38,26 @@ fun test3(p: Boolean) {
|
|||||||
<!DEBUG_INFO_SMARTCAST!>x<!>.length // ok
|
<!DEBUG_INFO_SMARTCAST!>x<!>.length // ok
|
||||||
c.x<!UNSAFE_CALL!>.<!>length // bad
|
c.x<!UNSAFE_CALL!>.<!>length // bad
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun test4(p: Boolean, q: Boolean) {
|
||||||
|
var c = C("...")
|
||||||
|
val x = c.x
|
||||||
|
if (x == null) return
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.length // ok
|
||||||
|
c.x<!UNSAFE_CALL!>.<!>length // ok
|
||||||
|
if (p) {
|
||||||
|
if (q) {
|
||||||
|
c = C(null)
|
||||||
|
} else {
|
||||||
|
c = C(null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (q) {
|
||||||
|
c = C(null)
|
||||||
|
} else {
|
||||||
|
c = C(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<!DEBUG_INFO_SMARTCAST!>x<!>.length // ok
|
||||||
|
c.x<!UNSAFE_CALL!>.<!>length // bad
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user