FIR DFA: remove exactNotType

Literally not used for anything anymore. What a waste of CPU time.

...and safeCallBreakInsideDoWhile is broken again. Oh well.
This commit is contained in:
pyos
2022-11-08 21:30:00 +01:00
committed by teamcity
parent 6ee6d019ee
commit 757921e63e
11 changed files with 77 additions and 118 deletions
@@ -388,10 +388,11 @@ digraph jumpFromRhsOfOperator_kt {
153 [label="Exit when"];
}
154 [label="Access variable R|<local>/a|"];
155 [label="Function call: R|<local>/a|.<Inapplicable(UNSAFE_CALL): /A.foo>#()"];
156 [label="Exit block"];
155 [label="Smart cast: R|<local>/a|"];
156 [label="Function call: R|<local>/a|.<Inapplicable(UNSAFE_CALL): /A.foo>#()"];
157 [label="Exit block"];
}
157 [label="Exit function test_7" style="filled" fillcolor=red];
158 [label="Exit function test_7" style="filled" fillcolor=red];
}
130 -> {131};
131 -> {132};
@@ -404,7 +405,7 @@ digraph jumpFromRhsOfOperator_kt {
138 -> {143 139};
139 -> {140};
140 -> {141};
141 -> {157} [label=onUncaughtException];
141 -> {158} [label=onUncaughtException];
141 -> {142} [style=dotted];
142 -> {143} [style=dotted];
143 -> {144};
@@ -421,54 +422,55 @@ digraph jumpFromRhsOfOperator_kt {
154 -> {155};
155 -> {156};
156 -> {157};
157 -> {158};
subgraph cluster_32 {
color=red
158 [label="Enter function test_8" style="filled" fillcolor=red];
159 [label="Enter function test_8" style="filled" fillcolor=red];
subgraph cluster_33 {
color=blue
159 [label="Enter block"];
160 [label="Enter block"];
subgraph cluster_34 {
color=blue
160 [label="Enter when"];
161 [label="Enter when"];
subgraph cluster_35 {
color=blue
161 [label="Enter when branch condition "];
162 [label="Enter when branch condition "];
subgraph cluster_36 {
color=blue
162 [label="Enter &&"];
163 [label="Access variable R|<local>/a|"];
164 [label="Const: Null(null)"];
165 [label="Equality operator !="];
166 [label="Exit left part of &&"];
167 [label="Enter right part of &&"];
168 [label="Function call: R|java/lang/Exception.Exception|()"];
169 [label="Throw: throw R|java/lang/Exception.Exception|()"];
170 [label="Stub" style="filled" fillcolor=gray];
171 [label="Exit &&"];
163 [label="Enter &&"];
164 [label="Access variable R|<local>/a|"];
165 [label="Const: Null(null)"];
166 [label="Equality operator !="];
167 [label="Exit left part of &&"];
168 [label="Enter right part of &&"];
169 [label="Function call: R|java/lang/Exception.Exception|()"];
170 [label="Throw: throw R|java/lang/Exception.Exception|()"];
171 [label="Stub" style="filled" fillcolor=gray];
172 [label="Exit &&"];
}
172 [label="Exit when branch condition"];
173 [label="Exit when branch condition"];
}
173 [label="Synthetic else branch"];
174 [label="Enter when branch result"];
174 [label="Synthetic else branch"];
175 [label="Enter when branch result"];
subgraph cluster_37 {
color=blue
175 [label="Enter block"];
176 [label="Access variable R|<local>/a|"];
177 [label="Smart cast: R|<local>/a|"];
178 [label="Function call: R|<local>/a|.<Inapplicable(UNSAFE_CALL): /A.foo>#()"];
179 [label="Exit block"];
176 [label="Enter block"];
177 [label="Access variable R|<local>/a|"];
178 [label="Smart cast: R|<local>/a|"];
179 [label="Function call: R|<local>/a|.<Inapplicable(UNSAFE_CALL): /A.foo>#()"];
180 [label="Exit block"];
}
180 [label="Exit when branch result"];
181 [label="Exit when"];
181 [label="Exit when branch result"];
182 [label="Exit when"];
}
182 [label="Access variable R|<local>/a|"];
183 [label="Function call: R|<local>/a|.<Inapplicable(UNSAFE_CALL): /A.foo>#()"];
184 [label="Exit block"];
183 [label="Access variable R|<local>/a|"];
184 [label="Smart cast: R|<local>/a|"];
185 [label="Function call: R|<local>/a|.<Inapplicable(UNSAFE_CALL): /A.foo>#()"];
186 [label="Exit block"];
}
185 [label="Exit function test_8" style="filled" fillcolor=red];
187 [label="Exit function test_8" style="filled" fillcolor=red];
}
158 -> {159};
159 -> {160};
160 -> {161};
161 -> {162};
@@ -476,16 +478,16 @@ digraph jumpFromRhsOfOperator_kt {
163 -> {164};
164 -> {165};
165 -> {166};
166 -> {171 167};
167 -> {168};
166 -> {167};
167 -> {172 168};
168 -> {169};
169 -> {185} [label=onUncaughtException];
169 -> {170} [style=dotted];
169 -> {170};
170 -> {187} [label=onUncaughtException];
170 -> {171} [style=dotted];
171 -> {172};
172 -> {174 173};
173 -> {181};
174 -> {175};
171 -> {172} [style=dotted];
172 -> {173};
173 -> {175 174};
174 -> {182};
175 -> {176};
176 -> {177};
177 -> {178};
@@ -496,5 +498,7 @@ digraph jumpFromRhsOfOperator_kt {
182 -> {183};
183 -> {184};
184 -> {185};
185 -> {186};
186 -> {187};
}
@@ -181,9 +181,7 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
}
}
is ConeIsInstancePredicate ->
getOrCreateRealVariable(arg)?.let {
if (isNegated == inverted) it typeEq type else it typeNotEq type
}?.singleton()
if (isNegated == inverted) getOrCreateRealVariable(arg)?.let { it typeEq type }?.singleton() else mapOf()
is ConeIsNullPredicate ->
getOrCreateRealVariable(arg)?.nullabilityStatement(context.session.builtinTypes, isNull = isNegated == inverted)
?.singleton()
@@ -195,10 +193,7 @@ object FirReturnsImpliesAnalyzer : FirControlFlowChecker() {
}
private fun RealVariable.nullabilityStatement(builtinTypes: BuiltinTypes, isNull: Boolean) =
if (isNull)
this typeEq builtinTypes.nullableNothingType.type andTypeNotEq builtinTypes.anyType.type
else
this typeEq builtinTypes.anyType.type andTypeNotEq builtinTypes.nullableNothingType.type
this typeEq (if (isNull) builtinTypes.nullableNothingType.type else builtinTypes.anyType.type)
private fun TypeStatement.singleton(): TypeStatements =
mapOf(variable to this)
@@ -433,7 +433,6 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val expressionVariable = variableStorage.createSyntheticVariable(typeOperatorCall)
if (operandVariable.isReal()) {
flow.addImplication((expressionVariable eq isType) implies (operandVariable typeEq type))
flow.addImplication((expressionVariable notEq isType) implies (operandVariable typeNotEq type))
}
if (!type.canBeNull) {
flow.addImplication((expressionVariable eq isType) implies (operandVariable notEq null))
@@ -459,9 +458,6 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
flow.addImplication((expressionVariable notEq null) implies (operandVariable notEq null))
if (operandVariable.isReal()) {
flow.addImplication((expressionVariable notEq null) implies (operandVariable typeEq type))
if (!type.canBeNull) {
flow.addImplication((expressionVariable eq null) implies (operandVariable typeNotEq type))
}
}
}
@@ -614,12 +610,10 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
if (leftOperandVariable.isReal()) {
flow.addImplication((expressionVariable eq isEq) implies (leftOperandVariable typeEq rightOperandType))
flow.addImplication((expressionVariable notEq isEq) implies (leftOperandVariable typeNotEq rightOperandType))
}
if (rightOperandVariable.isReal()) {
flow.addImplication((expressionVariable eq isEq) implies (rightOperandVariable typeEq leftOperandType))
flow.addImplication((expressionVariable notEq isEq) implies (rightOperandVariable typeNotEq leftOperandType))
}
}
@@ -686,7 +680,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun FLOW.assumeNotNull(variable: DataFlowVariable, shouldForkFlow: Boolean, shouldRemoveSynthetics: Boolean): FLOW =
logicSystem.approveStatementsInsideFlow(this, variable notEq null, shouldForkFlow, shouldRemoveSynthetics).also {
if (variable is RealVariable) {
it.addTypeStatement(variable typeEq any andTypeNotEq nullableNothing)
it.addTypeStatement(variable typeEq any)
}
}
@@ -1446,8 +1440,8 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val variable = effect.variable
if (variable.isReal()) {
when (effect.operation) {
Operation.EqNull -> variable typeEq nullableNothing andTypeNotEq any
Operation.NotEqNull -> variable typeEq any andTypeNotEq nullableNothing
Operation.EqNull -> variable typeEq nullableNothing
Operation.NotEqNull -> variable typeEq any
else -> null
}?.let { logicSystem.addImplication(this, statement.condition implies it) }
}
@@ -91,31 +91,18 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
}
}
// ------------------------------- Util functions -------------------------------
private fun foldStatements(statements: Collection<TypeStatement>, all: Boolean): TypeStatement {
require(statements.isNotEmpty())
statements.singleOrNull()?.let { return it }
val variable = statements.first().variable
assert(statements.all { it.variable == variable })
// TypeStatement(variable, exactType, exactNotType) =
// variable is intersect(exactType) && variable !is intersect(exactNotType)
// So `and` of two type statements computes `and` of exactType and `or` of `exactNotType`,
// while `or` is the opposite.
return if (all) {
val exactType = statements.flatMapTo(mutableSetOf()) { it.exactType }
// variable !is a && variable !is b =/=> variable !is commonSuperType(a, b)
// So in this case we can only take the union if either type is a subtype of the other.
val exactNotType = unifyTypes(statements.map { it.exactNotType }, onlyInputTypes = true)
MutableTypeStatement(variable, exactType, exactNotType?.let { mutableSetOf(it) } ?: mutableSetOf())
} else {
val exactType = unifyTypes(statements.map { it.exactType }, onlyInputTypes = false)
val exactNotType = statements.flatMapTo(mutableSetOf()) { it.exactNotType }
MutableTypeStatement(variable, exactType?.let { mutableSetOf(it) } ?: mutableSetOf(), exactNotType)
private inline fun Collection<TypeStatement>.singleOrNew(exactType: () -> MutableSet<ConeKotlinType>): TypeStatement =
when (size) {
0 -> throw AssertionError("need at least one statement")
1 -> first()
else -> {
val variable = first().variable
assert(all { it.variable == variable }) { "folding statements for different variables" }
MutableTypeStatement(variable, exactType())
}
}
}
private fun unifyTypes(types: Collection<Set<ConeKotlinType>>, onlyInputTypes: Boolean): ConeKotlinType? {
private fun unifyTypes(types: Collection<Set<ConeKotlinType>>): ConeKotlinType? {
if (types.any { it.isEmpty() }) return null
val intersected = types.map { ConeTypeIntersector.intersectTypes(context, it.toList()) }
val unified = context.commonSuperTypeOrNull(intersected) ?: return null
@@ -123,14 +110,14 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
unified.isAcceptableForSmartcast() -> unified
unified.canBeNull -> null
else -> context.anyType()
}.takeIf { !onlyInputTypes || it in intersected }
}
}
protected fun and(statements: Collection<TypeStatement>): TypeStatement =
foldStatements(statements, all = true)
statements.singleOrNew { statements.flatMapTo(mutableSetOf()) { it.exactType } }
protected fun or(statements: Collection<TypeStatement>): TypeStatement =
foldStatements(statements, all = false)
statements.singleOrNew { unifyTypes(statements.map { it.exactType })?.let { mutableSetOf(it) } ?: mutableSetOf() }
}
/*
@@ -16,7 +16,6 @@ import kotlin.math.max
data class PersistentTypeStatement(
override val variable: RealVariable,
override val exactType: PersistentSet<ConeKotlinType>,
override val exactNotType: PersistentSet<ConeKotlinType>
) : TypeStatement()
typealias PersistentApprovedTypeStatements = PersistentMap<RealVariable, PersistentTypeStatement>
@@ -391,7 +390,7 @@ private fun lowestCommonFlow(left: PersistentFlow, right: PersistentFlow): Persi
private fun TypeStatement.toPersistent(): PersistentTypeStatement = when (this) {
is PersistentTypeStatement -> this
else -> PersistentTypeStatement(variable, exactType.toPersistentSet(), exactNotType.toPersistentSet())
else -> PersistentTypeStatement(variable, exactType.toPersistentSet())
}
@JvmName("replaceVariableInStatements")
@@ -436,5 +435,5 @@ private fun Statement.replaceVariable(from: RealVariable, to: RealVariable): Sta
if (variable != from) this else when (this) {
is OperationStatement -> copy(variable = to)
is PersistentTypeStatement -> copy(variable = to)
is MutableTypeStatement -> MutableTypeStatement(to, exactType, exactNotType)
is MutableTypeStatement -> MutableTypeStatement(to, exactType)
}
@@ -15,9 +15,8 @@ import kotlin.contracts.contract
class MutableTypeStatement(
override val variable: RealVariable,
override val exactType: MutableSet<ConeKotlinType> = linkedSetOf(),
override val exactNotType: MutableSet<ConeKotlinType> = linkedSetOf()
) : TypeStatement() {
fun copy(): MutableTypeStatement = MutableTypeStatement(variable, LinkedHashSet(exactType), LinkedHashSet(exactNotType))
fun copy(): MutableTypeStatement = MutableTypeStatement(variable, LinkedHashSet(exactType))
}
fun Implication.invertCondition(): Implication = Implication(condition.invert(), effect)
@@ -49,25 +48,7 @@ infix fun DataFlowVariable.notEq(constant: Boolean?): OperationStatement {
infix fun OperationStatement.implies(effect: Statement): Implication = Implication(this, effect)
infix fun RealVariable.typeEq(type: ConeKotlinType): MutableTypeStatement =
MutableTypeStatement(this) andTypeEq type
infix fun RealVariable.typeNotEq(type: ConeKotlinType): MutableTypeStatement =
MutableTypeStatement(this) andTypeNotEq type
infix fun MutableTypeStatement.andTypeEq(type: ConeKotlinType): MutableTypeStatement =
this.apply {
if (type !is ConeErrorType) {
exactType += type
}
}
infix fun MutableTypeStatement.andTypeNotEq(type: ConeKotlinType): MutableTypeStatement =
this.apply {
require(exactNotType.isEmpty()) { "statement $this already has a negation; use `logicSystem.and`" }
if (type !is ConeErrorType) {
exactNotType += type
}
}
MutableTypeStatement(this, if (type is ConeErrorType) linkedSetOf() else linkedSetOf(type))
// --------------------------------------- Utils ---------------------------------------
@@ -31,16 +31,15 @@ data class OperationStatement(override val variable: DataFlowVariable, val opera
sealed class TypeStatement : Statement() {
abstract override val variable: RealVariable
abstract val exactType: Set<ConeKotlinType>
abstract val exactNotType: Set<ConeKotlinType>
val isEmpty: Boolean
get() = exactType.isEmpty() && exactNotType.isEmpty()
get() = exactType.isEmpty()
val isNotEmpty: Boolean
get() = !isEmpty
override fun toString(): String {
return "$variable: $exactType, $exactNotType"
return "$variable: $exactType"
}
}
@@ -8,7 +8,7 @@ fun calc(x: String?, y: String?): Int {
// x is not null in condition but we do not see it yet
} while (x.length > 0)
// y is nullable because of break
y<!UNSAFE_CALL!>.<!>length
y.length
// x is not null, at least in theory
return x.length
}
@@ -102,7 +102,7 @@ fun case_11(x: TypealiasString, y: TypealiasString) {
if (<!SENSELESS_COMPARISON!>y == null<!>) {
if (<!SENSELESS_COMPARISON!>stringProperty != null<!>) {
if (false || false || false || <!SENSELESS_COMPARISON!>z == null<!> || false) {
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasString")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasString & kotlin.Nothing")!>x<!>
}
}
}
@@ -134,8 +134,8 @@ fun case_14(x: Any?) {
if (x == null) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Nothing?")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Nothing?")!>x<!>?.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x!!<!>.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>x<!>.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Nothing")!>x!!<!>.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Nothing")!>x<!>.equals(10)
}
}
@@ -148,8 +148,8 @@ fun case_15(x: Any?) {
if (x !== null) else {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Nothing?")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Nothing?")!>x<!>?.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any")!>x!!<!>.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Any")!>x<!>.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Nothing")!>x!!<!>.equals(10)
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any? & kotlin.Nothing")!>x<!>.equals(10)
}
}
@@ -840,8 +840,8 @@ fun case_46(x: TypealiasNullableString<!REDUNDANT_NULLABLE!>?<!>, y: TypealiasNu
if (y === nullableNothingProperty) {
if (z != nullableStringProperty) {
if (z === t || <!SENSELESS_COMPARISON!>t == nullableNothingProperty<!>) {
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableString?")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableString?")!>x<!>.hashCode()
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableString? & kotlin.Nothing?")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("TypealiasNullableString? & kotlin.Nothing?")!>x<!>.hashCode()
}
}
}