FIR: Get rid of RealVariable.originalType

Store original type only for local variable aliases
The problem with the former is that RealVariable may be referenced
via safe-calls and via usual calls, but its type is fixed

^KT-38444 In Progress
This commit is contained in:
Denis Zharkov
2020-06-01 15:56:47 +03:00
parent b0b7cf4042
commit ec746e17c9
5 changed files with 44 additions and 26 deletions
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.transformSingle import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
class DataFlowAnalyzerContext<FLOW : Flow>( class DataFlowAnalyzerContext<FLOW : Flow>(
val graphBuilder: ControlFlowGraphBuilder, val graphBuilder: ControlFlowGraphBuilder,
@@ -118,8 +119,8 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
var variable = variableStorage.getRealVariableWithoutUnwrappingAlias(symbol, qualifiedAccessExpression, flow) ?: return null var variable = variableStorage.getRealVariableWithoutUnwrappingAlias(symbol, qualifiedAccessExpression, flow) ?: return null
val result = mutableListOf<ConeKotlinType>() val result = mutableListOf<ConeKotlinType>()
flow.directAliasMap[variable]?.let { flow.directAliasMap[variable]?.let {
result += it.originalType result.addIfNotNull(it.originalType)
variable = it variable = it.variable
} }
flow.getTypeStatement(variable)?.exactType?.let { result += it } flow.getTypeStatement(variable)?.exactType?.let { result += it }
return result.takeIf { it.isNotEmpty() } return result.takeIf { it.isNotEmpty() }
@@ -783,7 +784,10 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)?.let { initializerVariable -> variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)?.let { initializerVariable ->
logicSystem.addLocalVariableAlias(flow, propertyVariable, initializerVariable) logicSystem.addLocalVariableAlias(
flow, propertyVariable,
RealVariableAndType(initializerVariable, initializer.coneType)
)
// node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null)) // node.flow.addImplication((propertyVariable notEq null) implies (initializerVariable notEq null))
} }
@@ -15,12 +15,12 @@ abstract class Flow {
abstract fun getVariablesInTypeStatements(): Collection<RealVariable> abstract fun getVariablesInTypeStatements(): Collection<RealVariable>
abstract fun removeOperations(variable: DataFlowVariable): Collection<Implication> abstract fun removeOperations(variable: DataFlowVariable): Collection<Implication>
abstract val directAliasMap: Map<RealVariable, RealVariable> abstract val directAliasMap: Map<RealVariable, RealVariableAndType>
abstract val backwardsAliasMap: Map<RealVariable, List<RealVariable>> abstract val backwardsAliasMap: Map<RealVariable, List<RealVariable>>
} }
fun Flow.unwrapVariable(variable: RealVariable): RealVariable { fun Flow.unwrapVariable(variable: RealVariable): RealVariable {
return directAliasMap[variable] ?: variable return directAliasMap[variable]?.variable ?: variable
} }
abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) { abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceContext) {
@@ -53,7 +53,7 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
shouldRemoveSynthetics: Boolean, shouldRemoveSynthetics: Boolean,
): FLOW ): FLOW
abstract fun addLocalVariableAlias(flow: FLOW, alias: RealVariable, underlyingVariable: RealVariable) abstract fun addLocalVariableAlias(flow: FLOW, alias: RealVariable, underlyingVariable: RealVariableAndType)
abstract fun removeLocalVariableAlias(flow: FLOW, alias: RealVariable) abstract fun removeLocalVariableAlias(flow: FLOW, alias: RealVariable)
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication> protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
@@ -190,4 +190,4 @@ fun <F : Flow> LogicSystem<F>.replaceVariableFromConditionInStatements(
filter, filter,
transform, transform,
) )
} }
@@ -9,6 +9,7 @@ import com.google.common.collect.ArrayListMultimap
import kotlinx.collections.immutable.* import kotlinx.collections.immutable.*
import org.jetbrains.kotlin.fir.types.ConeInferenceContext 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 java.util.* import java.util.*
import kotlin.NoSuchElementException import kotlin.NoSuchElementException
@@ -51,7 +52,7 @@ class PersistentFlow : Flow {
* directAliasMap: { x -> a, y -> a} * directAliasMap: { x -> a, y -> a}
* backwardsAliasMap: { a -> [x, y] } * backwardsAliasMap: { a -> [x, y] }
*/ */
override var directAliasMap: PersistentMap<RealVariable, RealVariable> override var directAliasMap: PersistentMap<RealVariable, RealVariableAndType>
override var backwardsAliasMap: PersistentMap<RealVariable, PersistentList<RealVariable>> override var backwardsAliasMap: PersistentMap<RealVariable, PersistentList<RealVariable>>
constructor(previousFlow: PersistentFlow) { constructor(previousFlow: PersistentFlow) {
@@ -156,9 +157,9 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
private fun computeAliasesThatDontChange( private fun computeAliasesThatDontChange(
flows: Collection<PersistentFlow> flows: Collection<PersistentFlow>
): MutableMap<RealVariable, RealVariable> { ): MutableMap<RealVariable, RealVariableAndType> {
val flowsSize = flows.size val flowsSize = flows.size
val aliasedVariablesThatDontChangeAlias = mutableMapOf<RealVariable, RealVariable>() val aliasedVariablesThatDontChangeAlias = mutableMapOf<RealVariable, RealVariableAndType>()
flows.flatMapTo(mutableSetOf()) { it.directAliasMap.keys }.forEach { aliasedVariable -> flows.flatMapTo(mutableSetOf()) { it.directAliasMap.keys }.forEach { aliasedVariable ->
val originals = flows.map { it.directAliasMap[aliasedVariable] ?: return@forEach } val originals = flows.map { it.directAliasMap[aliasedVariable] ?: return@forEach }
@@ -173,7 +174,7 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
} }
private fun PersistentFlow.addVariableAliases( private fun PersistentFlow.addVariableAliases(
aliasedVariablesThatDontChangeAlias: MutableMap<RealVariable, RealVariable> aliasedVariablesThatDontChangeAlias: MutableMap<RealVariable, RealVariableAndType>
) { ) {
for ((alias, underlyingVariable) in aliasedVariablesThatDontChangeAlias) { for ((alias, underlyingVariable) in aliasedVariablesThatDontChangeAlias) {
addLocalVariableAlias(this, alias, underlyingVariable) addLocalVariableAlias(this, alias, underlyingVariable)
@@ -189,11 +190,11 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
} }
} }
override fun addLocalVariableAlias(flow: PersistentFlow, alias: RealVariable, underlyingVariable: RealVariable) { override fun addLocalVariableAlias(flow: PersistentFlow, alias: RealVariable, underlyingVariable: RealVariableAndType) {
removeLocalVariableAlias(flow, alias) removeLocalVariableAlias(flow, alias)
flow.directAliasMap = flow.directAliasMap.put(alias, underlyingVariable) flow.directAliasMap = flow.directAliasMap.put(alias, underlyingVariable)
flow.backwardsAliasMap = flow.backwardsAliasMap.put( flow.backwardsAliasMap = flow.backwardsAliasMap.put(
underlyingVariable, underlyingVariable.variable,
{ persistentListOf(alias) }, { persistentListOf(alias) },
{ variables -> variables + alias } { variables -> variables + alias }
) )
@@ -201,7 +202,7 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
override fun removeLocalVariableAlias(flow: PersistentFlow, alias: RealVariable) { override fun removeLocalVariableAlias(flow: PersistentFlow, alias: RealVariable) {
flow.updatedAliasDiff += alias flow.updatedAliasDiff += alias
val original = flow.directAliasMap[alias] ?: return val original = flow.directAliasMap[alias]?.variable ?: return
flow.directAliasMap = flow.directAliasMap.remove(alias) flow.directAliasMap = flow.directAliasMap.remove(alias)
val variables = flow.backwardsAliasMap.getValue(original) val variables = flow.backwardsAliasMap.getValue(original)
flow.backwardsAliasMap = flow.backwardsAliasMap.put(original, variables - alias) flow.backwardsAliasMap = flow.backwardsAliasMap.put(original, variables - alias)
@@ -220,8 +221,8 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
flow = flow.previousFlow!! flow = flow.previousFlow!!
} }
} else { } else {
result.exactType += variableUnderAlias.originalType result.exactType.addIfNotNull(variableUnderAlias.originalType)
flow.approvedTypeStatements[variableUnderAlias]?.let { result += it } flow.approvedTypeStatements[variableUnderAlias.variable]?.let { result += it }
} }
return result return result
} }
@@ -41,7 +41,7 @@ class VariableStorage(private val session: FirSession) {
private fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable { private fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir) val variable = getOrCreateRealVariableWithoutUnwrappingAlias(flow, symbol, fir)
return flow.directAliasMap[variable] ?: variable return flow.directAliasMap[variable]?.variable ?: variable
} }
private fun FirElement.unwrapElement(): FirElement = when (this) { private fun FirElement.unwrapElement(): FirElement = when (this) {
@@ -85,13 +85,7 @@ class VariableStorage(private val session: FirSession) {
} }
val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) } val receiverVariable = receiver?.let { getOrCreateVariable(flow, it) }
val originalType: ConeKotlinType = when (originalFir) { return RealVariable(identifier, isThisReference, receiverVariable, counter++)
is FirExpression -> originalFir.typeRef.coneTypeUnsafe()
is FirProperty -> originalFir.returnTypeRef.coneTypeUnsafe()
is FirVariableAssignment -> identifier.symbol.fir.extractReturnType()
else -> throw IllegalStateException("Should not be here: $originalFir")
}
return RealVariable(identifier, isThisReference, receiverVariable, originalType, counter++)
} }
@JvmName("getOrCreateRealVariableOrNull") @JvmName("getOrCreateRealVariableOrNull")
@@ -40,7 +40,6 @@ class RealVariable(
val identifier: Identifier, val identifier: Identifier,
val isThisReference: Boolean, val isThisReference: Boolean,
val explicitReceiverVariable: DataFlowVariable?, val explicitReceiverVariable: DataFlowVariable?,
val originalType: ConeKotlinType,
variableIndexForDebug: Int variableIndexForDebug: Int
) : DataFlowVariable(variableIndexForDebug) { ) : DataFlowVariable(variableIndexForDebug) {
override val isStable: Boolean by lazy { override val isStable: Boolean by lazy {
@@ -74,6 +73,26 @@ class RealVariable(
} }
} }
class RealVariableAndType(val variable: RealVariable, val originalType: ConeKotlinType?) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as RealVariableAndType
if (variable != other.variable) return false
if (originalType != other.originalType) return false
return true
}
override fun hashCode(): Int {
var result = variable.hashCode()
result = 31 * result + originalType.hashCode()
return result
}
}
class SyntheticVariable(val fir: FirElement, variableIndexForDebug: Int) : DataFlowVariable(variableIndexForDebug) { class SyntheticVariable(val fir: FirElement, variableIndexForDebug: Int) : DataFlowVariable(variableIndexForDebug) {
override val isStable: Boolean get() = true override val isStable: Boolean get() = true
@@ -220,4 +239,4 @@ infix fun RealVariable.typeNotEq(type: ConeKotlinType): TypeStatement =
MutableTypeStatement(this, linkedSetOf(), LinkedHashSet<ConeKotlinType>().apply { this += type }) MutableTypeStatement(this, linkedSetOf(), LinkedHashSet<ConeKotlinType>().apply { this += type })
} else { } else {
MutableTypeStatement(this) MutableTypeStatement(this)
} }