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:
+45
-41
@@ -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};
|
||||
|
||||
}
|
||||
|
||||
+2
-7
@@ -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)
|
||||
|
||||
+3
-9
@@ -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() }
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+2
-3
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user