[FIR] Fix clearing info about DF variable after reassignment

This commit is contained in:
Dmitriy Novozhilov
2021-03-04 11:50:56 +03:00
parent f8adce8b96
commit a6d1d47918
19 changed files with 181 additions and 15 deletions
@@ -3141,6 +3141,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt");
}
@TestMetadata("complexIfWithOr.kt")
public void testComplexIfWithOr() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/complexIfWithOr.kt");
}
@TestMetadata("safeCallAndEqualityToBool.kt")
public void testSafeCallAndEqualityToBool() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/safeCallAndEqualityToBool.kt");
@@ -0,0 +1,19 @@
FILE: complexIfWithOr.kt
public abstract interface State : R|kotlin/Any| {
}
public abstract interface Complex : R|kotlin/Any| {
public abstract val superClass: R|Complex?|
public get(): R|Complex?|
}
public abstract interface ExceptionState : R|State| {
}
public final fun test(qualifier: R|State?|): R|kotlin/Unit| {
when () {
==(R|<local>/qualifier|, Null(null)) || (R|<local>/qualifier| is R|ExceptionState|) || ==((R|<local>/qualifier| as? R|Complex|)?.{ $subj$.R|/Complex.superClass| }, Null(null)) -> {
^test Unit
}
}
R|<local>/qualifier|.R|/Complex.superClass|
}
@@ -0,0 +1,13 @@
interface State
interface Complex {
val superClass: Complex?
}
interface ExceptionState : State
fun test(qualifier: State?) {
if (qualifier == null || qualifier is ExceptionState || (qualifier as? Complex)?.superClass == null) {
return
}
qualifier.superClass
}
@@ -3520,6 +3520,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt");
}
@Test
@TestMetadata("complexIfWithOr.kt")
public void testComplexIfWithOr() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/complexIfWithOr.kt");
}
@Test
@TestMetadata("safeCallAndEqualityToBool.kt")
public void testSafeCallAndEqualityToBool() throws Exception {
@@ -3566,6 +3566,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/boundSafeCallAndIsCheck.kt");
}
@Test
@TestMetadata("complexIfWithOr.kt")
public void testComplexIfWithOr() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/smartcasts/safeCalls/complexIfWithOr.kt");
}
@Test
@TestMetadata("safeCallAndEqualityToBool.kt")
public void testSafeCallAndEqualityToBool() throws Exception {
@@ -14946,6 +14946,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/fir/FakeOverrideBuilder.kt");
}
@Test
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
@Test
@TestMetadata("Fir2IrClassifierStorage.kt")
public void testFir2IrClassifierStorage() throws Exception {
@@ -49,6 +49,12 @@ class DataFlowAnalyzerContext<FLOW : Flow>(
var variableStorage = variableStorage
private set
private var assignmentCounter = 0
fun newAssignmentIndex(): Int {
return assignmentCounter++
}
fun reset() {
graphBuilder.reset()
variablesForWhenConditions.clear()
@@ -84,7 +90,6 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private val receiverStack: PersistentImplicitReceiverStack
get() = components.implicitReceiverStack as PersistentImplicitReceiverStack
private val symbolProvider = components.session.symbolProvider
private val visibilityChecker = components.session.visibilityChecker
override val logicSystem: PersistentLogicSystem =
@@ -673,7 +678,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (possiblyChangedVariables.isEmpty()) return
val flow = node.flow
for (variable in possiblyChangedVariables) {
logicSystem.removeAllAboutVariableIncludingAliasInformation(flow, variable)
flow.removeAllAboutVariable(variable)
}
}
@@ -946,6 +951,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (isAssignment) {
logicSystem.removeLocalVariableAlias(flow, propertyVariable)
flow.removeAllAboutVariable(propertyVariable)
logicSystem.recordNewAssignment(flow, propertyVariable, context.newAssignmentIndex())
}
variableStorage.getOrCreateRealVariable(flow, initializer.symbol, initializer)?.let { initializerVariable ->
@@ -1256,7 +1262,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun FLOW.removeAllAboutVariable(variable: RealVariable?) {
if (variable == null) return
logicSystem.removeAllAboutVariable(this, variable)
logicSystem.removeTypeStatementsAboutVariable(this, variable)
logicSystem.removeLogicStatementsAboutVariable(this, variable)
logicSystem.removeAliasInformationAboutVariable(this, variable)
}
private fun FLOW.fork(): FLOW {
@@ -18,6 +18,7 @@ abstract class Flow {
abstract val directAliasMap: Map<RealVariable, RealVariableAndType>
abstract val backwardsAliasMap: Map<RealVariable, List<RealVariable>>
abstract val assignmentIndex: Map<RealVariable, Int>
}
fun Flow.unwrapVariable(variable: RealVariable): RealVariable {
@@ -36,8 +37,9 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
abstract fun addImplication(flow: FLOW, implication: Implication)
abstract fun removeAllAboutVariable(flow: FLOW, variable: RealVariable)
abstract fun removeAllAboutVariableIncludingAliasInformation(flow: FLOW, variable: RealVariable)
abstract fun removeTypeStatementsAboutVariable(flow: FLOW, variable: RealVariable)
abstract fun removeLogicStatementsAboutVariable(flow: FLOW, variable: DataFlowVariable)
abstract fun removeAliasInformationAboutVariable(flow: FLOW, variable: RealVariable)
abstract fun translateVariableFromConditionInStatements(
flow: FLOW,
@@ -58,6 +60,8 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
abstract fun addLocalVariableAlias(flow: FLOW, alias: RealVariable, underlyingVariable: RealVariableAndType)
abstract fun removeLocalVariableAlias(flow: FLOW, alias: RealVariable)
abstract fun recordNewAssignment(flow: FLOW, variable: RealVariable, index: Int)
protected abstract fun getImplicationsWithVariable(flow: FLOW, variable: DataFlowVariable): Collection<Implication>
protected abstract fun ConeKotlinType.isAcceptableForSmartcast(): Boolean
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.fir.types.ConeInferenceContext
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
import kotlin.NoSuchElementException
data class PersistentTypeStatement(
override val variable: RealVariable,
@@ -55,6 +54,8 @@ class PersistentFlow : Flow {
override var directAliasMap: PersistentMap<RealVariable, RealVariableAndType>
override var backwardsAliasMap: PersistentMap<RealVariable, PersistentList<RealVariable>>
override var assignmentIndex: PersistentMap<RealVariable, Int>
constructor(previousFlow: PersistentFlow) {
this.previousFlow = previousFlow
approvedTypeStatements = previousFlow.approvedTypeStatements
@@ -63,6 +64,7 @@ class PersistentFlow : Flow {
directAliasMap = previousFlow.directAliasMap
backwardsAliasMap = previousFlow.backwardsAliasMap
assignmentIndex = previousFlow.assignmentIndex
}
constructor() {
@@ -73,6 +75,7 @@ class PersistentFlow : Flow {
directAliasMap = persistentMapOf()
backwardsAliasMap = persistentMapOf()
assignmentIndex = persistentMapOf()
}
override fun getTypeStatement(variable: RealVariable): TypeStatement? {
@@ -133,7 +136,12 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
val variables = flows.flatMap { it.approvedTypeStatements.keys }.toSet()
for (variable in variables) {
val info = mergeOperation(flows.map { it.getApprovedTypeStatements(variable, commonFlow) }) ?: continue
removeAllAboutVariable(commonFlow, variable)
removeTypeStatementsAboutVariable(commonFlow, variable)
val thereWereReassignments = variable.hasDifferentReassignments(flows)
if (thereWereReassignments) {
removeLogicStatementsAboutVariable(commonFlow, variable)
removeAliasInformationAboutVariable(commonFlow, variable)
}
commonFlow.addApprovedStatements(info)
}
@@ -141,6 +149,15 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
return commonFlow
}
private fun RealVariable.hasDifferentReassignments(flows: Collection<PersistentFlow>): Boolean {
val firstIndex = flows.first().assignmentIndex[this] ?: -1
for (flow in flows) {
val index = flow.assignmentIndex[this] ?: -1
if (index != firstIndex) return true
}
return false
}
private fun computeAliasesThatDontChange(
flows: Collection<PersistentFlow>
): MutableMap<RealVariable, RealVariableAndType> {
@@ -247,15 +264,33 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
}
}
override fun removeAllAboutVariable(flow: PersistentFlow, variable: RealVariable) {
flow.logicStatements -= variable
override fun removeTypeStatementsAboutVariable(flow: PersistentFlow, variable: RealVariable) {
flow.approvedTypeStatements -= variable
flow.approvedTypeStatementsDiff -= variable
// TODO: should we search variable in all logic statements?
}
override fun removeAllAboutVariableIncludingAliasInformation(flow: PersistentFlow, variable: RealVariable) {
removeAllAboutVariable(flow, variable)
override fun removeLogicStatementsAboutVariable(flow: PersistentFlow, variable: DataFlowVariable) {
flow.logicStatements -= variable
var newLogicStatements = flow.logicStatements
for ((key, implications) in flow.logicStatements) {
val implicationsToDelete = mutableListOf<Implication>()
implications.forEach { implication ->
if (implication.effect.variable == variable) {
implicationsToDelete += implication
}
}
if (implicationsToDelete.isEmpty()) continue
val newImplications = implications.removeAll(implicationsToDelete)
newLogicStatements = if (newImplications.isNotEmpty()) {
newLogicStatements.put(key, newImplications)
} else {
newLogicStatements.remove(key)
}
}
flow.logicStatements = newLogicStatements
}
override fun removeAliasInformationAboutVariable(flow: PersistentFlow, variable: RealVariable) {
val existedAlias = flow.directAliasMap[variable]?.variable
if (existedAlias != null) {
flow.directAliasMap = flow.directAliasMap.remove(variable)
@@ -408,6 +443,10 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
return flow.logicStatements[variable] ?: emptyList()
}
override fun recordNewAssignment(flow: PersistentFlow, variable: RealVariable, index: Int) {
flow.assignmentIndex = flow.assignmentIndex.put(variable, index)
}
// --------------------------------------------------------------------\
}
@@ -128,6 +128,7 @@ private infix fun FirElement.isEqualsTo(other: FirElement): Boolean {
sealed class Statement<T : Statement<T>> {
abstract fun invert(): T
abstract val variable: DataFlowVariable
}
/*
@@ -137,7 +138,7 @@ sealed class Statement<T : Statement<T>> {
* d == True
* d == False
*/
data class OperationStatement(val variable: DataFlowVariable, val operation: Operation) : Statement<OperationStatement>() {
data class OperationStatement(override val variable: DataFlowVariable, val operation: Operation) : Statement<OperationStatement>() {
override fun invert(): OperationStatement {
return OperationStatement(variable, operation.invert())
}
@@ -148,7 +149,7 @@ data class OperationStatement(val variable: DataFlowVariable, val operation: Ope
}
abstract class TypeStatement : Statement<TypeStatement>() {
abstract val variable: RealVariable
abstract override val variable: RealVariable
abstract val exactType: Set<ConeKotlinType>
abstract val exactNotType: Set<ConeKotlinType>
@@ -209,6 +210,8 @@ fun Implication.invertCondition(): Implication = Implication(condition.invert(),
typealias TypeStatements = Map<RealVariable, TypeStatement>
typealias MutableTypeStatements = MutableMap<RealVariable, MutableTypeStatement>
typealias MutableOperationStatements = MutableMap<RealVariable, MutableTypeStatement>
fun MutableTypeStatements.addStatement(variable: RealVariable, statement: TypeStatement) {
put(variable, statement.asMutableStatement()) { it.apply { this += statement } }
}
@@ -0,0 +1,21 @@
abstract class A {
abstract fun foo(): String
}
class B : A() {
override fun foo() = "OK"
}
class C : A() {
override fun foo() = "fail"
}
fun test(c: C, cond: Boolean): String {
var x: A = c
if (cond) {
x = B()
}
return x.foo()
}
fun box(): String = test(C(), true)
@@ -1,4 +1,3 @@
// IGNORE_BACKEND_FIR: JVM_IR
// FILE: 1.kt
package test
@@ -14946,6 +14946,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/fir/FakeOverrideBuilder.kt");
}
@Test
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
@Test
@TestMetadata("Fir2IrClassifierStorage.kt")
public void testFir2IrClassifierStorage() throws Exception {
@@ -14946,6 +14946,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/fir/FakeOverrideBuilder.kt");
}
@Test
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
@Test
@TestMetadata("Fir2IrClassifierStorage.kt")
public void testFir2IrClassifierStorage() throws Exception {
@@ -12338,6 +12338,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/fir/FakeOverrideBuilder.kt");
}
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
@TestMetadata("Fir2IrClassifierStorage.kt")
public void testFir2IrClassifierStorage() throws Exception {
runTest("compiler/testData/codegen/box/fir/Fir2IrClassifierStorage.kt");
@@ -10945,6 +10945,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
public void testClassCanNotBeCastedToVoid() throws Exception {
runTest("compiler/testData/codegen/box/fir/classCanNotBeCastedToVoid.kt");
}
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/fullJdk")
@@ -10402,6 +10402,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
public void testClassCanNotBeCastedToVoid() throws Exception {
runTest("compiler/testData/codegen/box/fir/classCanNotBeCastedToVoid.kt");
}
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/fullJdk")
@@ -10402,6 +10402,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
public void testClassCanNotBeCastedToVoid() throws Exception {
runTest("compiler/testData/codegen/box/fir/classCanNotBeCastedToVoid.kt");
}
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/fullJdk")
@@ -5233,6 +5233,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
public void testClassCanNotBeCastedToVoid() throws Exception {
runTest("compiler/testData/codegen/box/fir/classCanNotBeCastedToVoid.kt");
}
@TestMetadata("falsePositiveBoundSmartcast.kt")
public void testFalsePositiveBoundSmartcast() throws Exception {
runTest("compiler/testData/codegen/box/fir/falsePositiveBoundSmartcast.kt");
}
}
@TestMetadata("compiler/testData/codegen/box/fullJdk")