[FIR] Support PreliminaryLoopVisitor in FIR DFA
This commit is contained in:
+6
@@ -37516,6 +37516,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaArgumentWithoutType.kt")
|
||||
public void testLambdaArgumentWithoutType() throws Exception {
|
||||
|
||||
+32
-4
@@ -29,19 +29,20 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
|
||||
class DataFlowAnalyzerContext<FLOW : Flow>(
|
||||
val graphBuilder: ControlFlowGraphBuilder,
|
||||
variableStorage: VariableStorage,
|
||||
flowOnNodes: MutableMap<CFGNode<*>, FLOW>,
|
||||
val variablesForWhenConditions: MutableMap<WhenBranchConditionExitNode, DataFlowVariable>
|
||||
val variablesForWhenConditions: MutableMap<WhenBranchConditionExitNode, DataFlowVariable>,
|
||||
val preliminaryLoopVisitor: PreliminaryLoopVisitor
|
||||
) {
|
||||
var flowOnNodes = flowOnNodes
|
||||
private set
|
||||
@@ -54,13 +55,15 @@ class DataFlowAnalyzerContext<FLOW : Flow>(
|
||||
|
||||
variableStorage = variableStorage.clear()
|
||||
flowOnNodes = mutableMapOf()
|
||||
|
||||
preliminaryLoopVisitor.resetState()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun <FLOW : Flow> empty(session: FirSession) =
|
||||
fun <FLOW : Flow> empty(session: FirSession): DataFlowAnalyzerContext<FLOW> =
|
||||
DataFlowAnalyzerContext<FLOW>(
|
||||
ControlFlowGraphBuilder(), VariableStorage(session),
|
||||
mutableMapOf(), mutableMapOf()
|
||||
mutableMapOf(), mutableMapOf(), PreliminaryLoopVisitor()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -210,12 +213,14 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
// TODO: questionable
|
||||
postponedLambdaExitNode?.mergeIncomingFlow()
|
||||
functionExitNode.mergeIncomingFlow()
|
||||
exitCapturingStatement(anonymousFunction)
|
||||
return FirControlFlowGraphReferenceImpl(graph)
|
||||
}
|
||||
|
||||
fun visitPostponedAnonymousFunction(anonymousFunction: FirAnonymousFunction) {
|
||||
val (enterNode, exitNode) = graphBuilder.visitPostponedAnonymousFunction(anonymousFunction)
|
||||
enterNode.mergeIncomingFlow()
|
||||
enterCapturingStatement(enterNode, anonymousFunction)
|
||||
exitNode.mergeIncomingFlow()
|
||||
enterNode.flow = enterNode.flow.fork()
|
||||
}
|
||||
@@ -236,12 +241,14 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
}
|
||||
|
||||
private fun exitLocalClass(klass: FirRegularClass): ControlFlowGraph {
|
||||
// TODO: support capturing of mutable properties, KT-44877
|
||||
val (node, controlFlowGraph) = graphBuilder.exitLocalClass(klass)
|
||||
node.mergeIncomingFlow()
|
||||
return controlFlowGraph
|
||||
}
|
||||
|
||||
fun exitAnonymousObject(anonymousObject: FirAnonymousObject): ControlFlowGraph {
|
||||
// TODO: support capturing of mutable properties, KT-44877
|
||||
val (node, controlFlowGraph) = graphBuilder.exitAnonymousObject(anonymousObject)
|
||||
node.mergeIncomingFlow()
|
||||
return controlFlowGraph
|
||||
@@ -601,11 +608,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
}
|
||||
exitCapturingStatement(exitNode.fir)
|
||||
}
|
||||
|
||||
fun enterWhileLoop(loop: FirLoop) {
|
||||
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
|
||||
loopEnterNode.mergeIncomingFlow()
|
||||
enterCapturingStatement(loopEnterNode, loop)
|
||||
loopConditionEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
@@ -630,11 +639,30 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
exitCommonLoop(exitNode)
|
||||
}
|
||||
|
||||
private fun enterCapturingStatement(node: CFGNode<*>, statement: FirStatement) {
|
||||
val reassignedNames = context.preliminaryLoopVisitor.enterCapturingStatement(statement)
|
||||
if (reassignedNames.isEmpty()) return
|
||||
val possiblyChangedVariables = variableStorage.realVariables.filterKeys {
|
||||
val fir = (it.symbol as? FirVariableSymbol<*>)?.fir ?: return@filterKeys false
|
||||
fir.isVar && fir.name in reassignedNames
|
||||
}.values
|
||||
if (possiblyChangedVariables.isEmpty()) return
|
||||
val flow = node.flow
|
||||
for (variable in possiblyChangedVariables) {
|
||||
logicSystem.removeAllAboutVariableIncludingAliasInformation(flow, variable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitCapturingStatement(statement: FirStatement) {
|
||||
context.preliminaryLoopVisitor.exitCapturingStatement(statement)
|
||||
}
|
||||
|
||||
// ----------------------------------- Do while Loop -----------------------------------
|
||||
|
||||
fun enterDoWhileLoop(loop: FirLoop) {
|
||||
val (loopEnterNode, loopBlockEnterNode) = graphBuilder.enterDoWhileLoop(loop)
|
||||
loopEnterNode.mergeIncomingFlow()
|
||||
enterCapturingStatement(loopEnterNode, loop)
|
||||
loopBlockEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ 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 translateVariableFromConditionInStatements(
|
||||
flow: FLOW,
|
||||
|
||||
+15
@@ -254,6 +254,21 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
|
||||
// TODO: should we search variable in all logic statements?
|
||||
}
|
||||
|
||||
override fun removeAllAboutVariableIncludingAliasInformation(flow: PersistentFlow, variable: RealVariable) {
|
||||
removeAllAboutVariable(flow, variable)
|
||||
val existedAlias = flow.directAliasMap[variable]?.variable
|
||||
if (existedAlias != null) {
|
||||
flow.directAliasMap = flow.directAliasMap.remove(variable)
|
||||
val updatedBackwardsAliasList = flow.backwardsAliasMap.getValue(existedAlias).remove(variable)
|
||||
flow.backwardsAliasMap = if (updatedBackwardsAliasList.isEmpty()) {
|
||||
flow.backwardsAliasMap.remove(existedAlias)
|
||||
} else {
|
||||
flow.backwardsAliasMap.put(existedAlias, updatedBackwardsAliasList)
|
||||
}
|
||||
flow.updatedAliasDiff = flow.updatedAliasDiff.add(variable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun translateVariableFromConditionInStatements(
|
||||
flow: PersistentFlow,
|
||||
originalVariable: DataFlowVariable,
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.util.SetMultimap
|
||||
import org.jetbrains.kotlin.fir.util.setMultimapOf
|
||||
import org.jetbrains.kotlin.fir.visitors.FirVisitor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
class PreliminaryLoopVisitor {
|
||||
private val reassignedVariablesPerElement: SetMultimap<FirStatement, Name> = setMultimapOf()
|
||||
|
||||
fun enterCapturingStatement(statement: FirStatement): Set<Name> {
|
||||
assert(statement is FirLoop || statement is FirClass<*> || statement is FirFunction<*>)
|
||||
if (statement !in reassignedVariablesPerElement) {
|
||||
statement.accept(visitor, null)
|
||||
}
|
||||
return reassignedVariablesPerElement[statement]
|
||||
}
|
||||
|
||||
fun exitCapturingStatement(statement: FirStatement) {
|
||||
assert(statement is FirLoop || statement is FirClass<*> || statement is FirFunction<*>)
|
||||
reassignedVariablesPerElement.removeKey(statement)
|
||||
}
|
||||
|
||||
fun resetState() {
|
||||
reassignedVariablesPerElement.clear()
|
||||
}
|
||||
|
||||
// FirStatement -- closest statement (loop/lambda/local declaration) which may contain reassignments
|
||||
private val visitor = object : FirVisitor<Unit, FirStatement?>() {
|
||||
override fun visitElement(element: FirElement, data: FirStatement?) {
|
||||
element.acceptChildren(this, data)
|
||||
}
|
||||
|
||||
override fun visitVariableAssignment(variableAssignment: FirVariableAssignment, data: FirStatement?) {
|
||||
val reference = variableAssignment.lValue as? FirNamedReference
|
||||
if (reference != null) {
|
||||
requireNotNull(data)
|
||||
reassignedVariablesPerElement.put(data, reference.name)
|
||||
}
|
||||
visitElement(variableAssignment, data)
|
||||
}
|
||||
|
||||
override fun visitWhileLoop(whileLoop: FirWhileLoop, data: FirStatement?) {
|
||||
visitCapturingStatement(whileLoop, data)
|
||||
}
|
||||
|
||||
override fun visitDoWhileLoop(doWhileLoop: FirDoWhileLoop, data: FirStatement?) {
|
||||
visitCapturingStatement(doWhileLoop, data)
|
||||
}
|
||||
|
||||
override fun visitAnonymousFunction(anonymousFunction: FirAnonymousFunction, data: FirStatement?) {
|
||||
visitCapturingStatement(anonymousFunction, data)
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(simpleFunction: FirSimpleFunction, data: FirStatement?) {
|
||||
visitCapturingStatement(simpleFunction, data)
|
||||
}
|
||||
|
||||
override fun <F : FirFunction<F>> visitFunction(function: FirFunction<F>, data: FirStatement?) {
|
||||
visitCapturingStatement(function, data)
|
||||
}
|
||||
|
||||
override fun visitRegularClass(regularClass: FirRegularClass, data: FirStatement?) {
|
||||
visitCapturingStatement(regularClass, data)
|
||||
}
|
||||
|
||||
override fun visitAnonymousObject(anonymousObject: FirAnonymousObject, data: FirStatement?) {
|
||||
visitCapturingStatement(anonymousObject, data)
|
||||
}
|
||||
|
||||
private fun visitCapturingStatement(statement: FirStatement, parent: FirStatement?) {
|
||||
visitElement(statement, statement)
|
||||
if (parent != null) {
|
||||
reassignedVariablesPerElement.putAll(parent, reassignedVariablesPerElement[statement])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,10 @@ import kotlin.contracts.contract
|
||||
@OptIn(DfaInternals::class)
|
||||
class VariableStorage(private val session: FirSession) {
|
||||
private var counter = 1
|
||||
private val realVariables: MutableMap<Identifier, RealVariable> = HashMap()
|
||||
private val _realVariables: MutableMap<Identifier, RealVariable> = HashMap()
|
||||
val realVariables: Map<Identifier, RealVariable>
|
||||
get() = _realVariables
|
||||
|
||||
private val syntheticVariables: MutableMap<FirElement, SyntheticVariable> = HashMap()
|
||||
|
||||
fun clear(): VariableStorage = VariableStorage(session)
|
||||
@@ -34,7 +37,7 @@ class VariableStorage(private val session: FirSession) {
|
||||
fun getOrCreateRealVariableWithoutUnwrappingAlias(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
|
||||
val realFir = fir.unwrapElement()
|
||||
val identifier = getIdentifierBySymbol(flow, symbol, realFir)
|
||||
return realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir) }
|
||||
return _realVariables.getOrPut(identifier) { createRealVariableInternal(flow, identifier, realFir) }
|
||||
}
|
||||
|
||||
private fun getOrCreateRealVariable(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
|
||||
@@ -108,7 +111,7 @@ class VariableStorage(private val session: FirSession) {
|
||||
fun getRealVariableWithoutUnwrappingAlias(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
|
||||
val realFir = fir.unwrapElement()
|
||||
return symbol.takeIf { it.isStable(realFir) }?.let {
|
||||
realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())]
|
||||
_realVariables[getIdentifierBySymbol(flow, it, realFir.unwrapElement())]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +134,7 @@ class VariableStorage(private val session: FirSession) {
|
||||
}
|
||||
|
||||
fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) {
|
||||
realVariables.remove(Identifier(symbol, null, null))
|
||||
_realVariables.remove(Identifier(symbol, null, null))
|
||||
}
|
||||
|
||||
fun removeSyntheticVariable(variable: DataFlowVariable) {
|
||||
|
||||
+1
-2
@@ -1,6 +1,5 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
|
||||
open class A {
|
||||
fun Foo() {
|
||||
@@ -23,4 +22,4 @@ fun box(): String {
|
||||
test.Foo()
|
||||
|
||||
return "OK"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// ISSUE: KT-44804
|
||||
// WITH_STDLIB
|
||||
|
||||
abstract class AbstractInsnNode(val next: AbstractInsnNode? = null)
|
||||
|
||||
class LineNumberNode(next: AbstractInsnNode? = null) : AbstractInsnNode(next) {
|
||||
val line: Int = 1
|
||||
}
|
||||
|
||||
class LabelNode() : AbstractInsnNode(null)
|
||||
|
||||
fun isDeadLineNumber(insn: LineNumberNode, index: Int, frames: Array<out Any?>): Boolean {
|
||||
// Line number node is "dead" if the corresponding line number interval
|
||||
// contains at least one "dead" meaningful instruction and no "live" meaningful instructions.
|
||||
var finger: AbstractInsnNode = insn
|
||||
var fingerIndex = index
|
||||
var hasDeadInsn = false
|
||||
loop@ while (true) {
|
||||
finger = finger.next ?: break
|
||||
fingerIndex++
|
||||
when (finger) {
|
||||
is LabelNode ->
|
||||
continue@loop
|
||||
is LineNumberNode ->
|
||||
if (finger.line != insn.line) return hasDeadInsn
|
||||
else -> {
|
||||
if (frames[fingerIndex] != null) return false
|
||||
hasDeadInsn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val node = LineNumberNode(
|
||||
LineNumberNode(
|
||||
LabelNode()
|
||||
)
|
||||
)
|
||||
val result = isDeadLineNumber(node, 0, arrayOf(null, null, "aaa", "bbb"))
|
||||
return if (result) "OK" else "fail"
|
||||
}
|
||||
@@ -10,5 +10,5 @@ fun use() {
|
||||
// Write to x is AFTER
|
||||
x.hashCode()
|
||||
// No smart cast should be here!
|
||||
foo(bar { x = null }, x.hashCode())
|
||||
foo(bar { x = null }, x<!UNSAFE_CALL!>.<!>hashCode())
|
||||
}
|
||||
|
||||
+2
-2
@@ -4,10 +4,10 @@ fun foo(arg: Int?) {
|
||||
if (x == null) return
|
||||
run {
|
||||
// Unsafe because of owner modification
|
||||
x.hashCode()
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
x = null
|
||||
}
|
||||
if (x != null) x = 42
|
||||
// Unsafe because of lambda
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -6,5 +6,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {}
|
||||
fun bar() {
|
||||
var x: Int?
|
||||
x = 4
|
||||
foo(x, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, x)
|
||||
<!INAPPLICABLE_CANDIDATE!>foo<!>(x, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, x)
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ public fun foo() {
|
||||
var i: Any = 1
|
||||
if (i is Int) {
|
||||
while (i != 10) {
|
||||
i++ // Here smart cast should not be performed due to a successor
|
||||
i<!UNRESOLVED_REFERENCE!>++<!> // Here smart cast should not be performed due to a successor
|
||||
i = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ public fun foo() {
|
||||
var i: Any = 1
|
||||
if (i is Int) {
|
||||
while (i != 10) {
|
||||
i++
|
||||
i<!UNRESOLVED_REFERENCE!>++<!>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,5 +13,5 @@ fun list(start: String) {
|
||||
e = e.next()
|
||||
}
|
||||
// e can never be null but we do not know it
|
||||
e.hashCode()
|
||||
}
|
||||
e<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,7 +13,7 @@ fun foo(): Bar {
|
||||
y = Bar()
|
||||
while (x != null) {
|
||||
// Here call is unsafe because of inner loop
|
||||
y.next()
|
||||
y<!UNSAFE_CALL!>.<!>next()
|
||||
while (y != null) {
|
||||
if (x == y)
|
||||
// x is not null because of outer while
|
||||
@@ -25,4 +25,4 @@ fun foo(): Bar {
|
||||
x = x.next()
|
||||
}
|
||||
return Bar()
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+4
-4
@@ -30,7 +30,7 @@ fun baz(s: String?) {
|
||||
x.hashCode()
|
||||
}
|
||||
run {
|
||||
x.hashCode()
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
x = null
|
||||
}
|
||||
}
|
||||
@@ -40,11 +40,11 @@ fun gaz(s: String?) {
|
||||
var x = s
|
||||
if (x != null) {
|
||||
run {
|
||||
x.hashCode()
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
x = null
|
||||
}
|
||||
run {
|
||||
x.hashCode()
|
||||
x<!UNSAFE_CALL!>.<!>hashCode()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,4 +57,4 @@ fun gav(s: String?) {
|
||||
}
|
||||
x = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
data class SomeObject(val n: SomeObject?) {
|
||||
fun doSomething(): Boolean = true
|
||||
fun next(): SomeObject? = n
|
||||
}
|
||||
|
||||
|
||||
fun list(start: SomeObject) {
|
||||
var e: SomeObject?
|
||||
e = start
|
||||
do {
|
||||
// In theory smart cast is possible here
|
||||
// But in practice we have a loop with changing e
|
||||
// ?: should we "or" entrance type info with condition type info?
|
||||
if (!e.doSomething())
|
||||
break
|
||||
// Smart cast here is still not possible
|
||||
e = e.next()
|
||||
} while (e != null)
|
||||
// e can be null because of next()
|
||||
e.doSomething()
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
data class SomeObject(val n: SomeObject?) {
|
||||
fun doSomething(): Boolean = true
|
||||
fun next(): SomeObject? = n
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
fun x(): Boolean { return true }
|
||||
|
||||
public fun foo(pp: String?): Int {
|
||||
var p = pp
|
||||
do {
|
||||
p!!.length
|
||||
if (p == "abc") break
|
||||
p = null
|
||||
} while (!x())
|
||||
// Smart cast is NOT possible here
|
||||
return p.length
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
fun x(): Boolean { return true }
|
||||
|
||||
public fun foo(pp: String?): Int {
|
||||
|
||||
@@ -9,9 +9,9 @@ fun list(start: SomeObject): SomeObject {
|
||||
var e: SomeObject? = start
|
||||
for (i in 0..42) {
|
||||
// Unsafe calls because of nullable e at the beginning
|
||||
e.doSomething()
|
||||
e = e.next()
|
||||
e<!UNSAFE_CALL!>.<!>doSomething()
|
||||
e = e<!UNSAFE_CALL!>.<!>next()
|
||||
}
|
||||
// Smart cast is not possible here due to next()
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,5 +16,5 @@ fun list(start: SomeObject) {
|
||||
e = e.next()
|
||||
}
|
||||
// e can be null because of next()
|
||||
e.doSomething()
|
||||
}
|
||||
e<!UNSAFE_CALL!>.<!>doSomething()
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ public fun foo() {
|
||||
var i: Int? = 1
|
||||
if (i != null) {
|
||||
while (i != 10) {
|
||||
i++ // Here smart cast should not be performed due to a successor
|
||||
i<!UNSAFE_CALL!>++<!> // Here smart cast should not be performed due to a successor
|
||||
i = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ public fun foo() {
|
||||
var i: Int? = 1
|
||||
if (i != null) {
|
||||
while (i != 10) {
|
||||
i++
|
||||
i<!UNSAFE_CALL!>++<!>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -13,5 +13,5 @@ fun list(start: SomeObject) {
|
||||
e = e.next()
|
||||
}
|
||||
// e can be null because of next()
|
||||
e.doSomething()
|
||||
}
|
||||
e<!UNSAFE_CALL!>.<!>doSomething()
|
||||
}
|
||||
|
||||
+6
@@ -37516,6 +37516,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaArgumentWithoutType.kt")
|
||||
public void testLambdaArgumentWithoutType() throws Exception {
|
||||
|
||||
+6
@@ -37516,6 +37516,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaArgumentWithoutType.kt")
|
||||
public void testLambdaArgumentWithoutType() throws Exception {
|
||||
|
||||
+5
@@ -29984,6 +29984,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentWithoutType.kt")
|
||||
public void testLambdaArgumentWithoutType() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
|
||||
|
||||
@@ -19,8 +19,8 @@ fun case_1() {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ fun case_2() {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +107,8 @@ fun case_5() {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,12 +240,12 @@ fun case_12() {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
while (if (true) { b = a; true } else true) {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
|
||||
}
|
||||
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,8 +276,8 @@ fun case_14() {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
while (true) {
|
||||
if (true) { b = a; } else 3
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ fun case_1() {
|
||||
var x: Any? = null
|
||||
if (x == null) return
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x != null)
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ fun case_2() {
|
||||
var x: Any? = null
|
||||
if (x === null) return
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x !== null)
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ fun case_5() {
|
||||
var x: Any? = null
|
||||
if (x == null) return
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x !== null)
|
||||
}
|
||||
|
||||
@@ -71,8 +71,8 @@ fun case_6() {
|
||||
var x: Any? = null
|
||||
if (x === null) return
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x != null)
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ fun case_7() {
|
||||
var x: Any? = null
|
||||
x ?: return
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x !== null)
|
||||
}
|
||||
|
||||
@@ -99,8 +99,8 @@ fun case_8() {
|
||||
var x: Any? = null
|
||||
if (x == null) throw Exception()
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x != null)
|
||||
}
|
||||
|
||||
@@ -113,8 +113,8 @@ fun case_9() {
|
||||
var x: Any? = null
|
||||
x ?: throw Exception()
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x !== null)
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ fun case_10() {
|
||||
var x: Any? = null
|
||||
x as Any
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x != null)
|
||||
}
|
||||
|
||||
@@ -155,8 +155,8 @@ fun case_12() {
|
||||
var x: Any? = null
|
||||
if (x is Any) {
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x .equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x <!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x != null)
|
||||
}
|
||||
}
|
||||
@@ -170,8 +170,8 @@ fun case_13() {
|
||||
var x: Any? = null
|
||||
if (x != null) {
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x .equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x <!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x != null)
|
||||
}
|
||||
}
|
||||
@@ -185,8 +185,8 @@ fun case_14() {
|
||||
var x: Any? = null
|
||||
if (x == null) return
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x.equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x<!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x is Any)
|
||||
}
|
||||
|
||||
@@ -199,8 +199,8 @@ fun case_15() {
|
||||
var x: Any? = null
|
||||
if (x is Any) {
|
||||
do {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!>
|
||||
x = x .equals(10)
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
|
||||
x = x <!UNSAFE_CALL!>.<!>equals(10)
|
||||
} while (x is Any)
|
||||
}
|
||||
}
|
||||
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java
Generated
+5
@@ -25530,6 +25530,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentWithoutType.kt")
|
||||
public void testLambdaArgumentWithoutType() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
|
||||
|
||||
Generated
+5
@@ -25015,6 +25015,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentWithoutType.kt")
|
||||
public void testLambdaArgumentWithoutType() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
|
||||
|
||||
Generated
+5
@@ -24975,6 +24975,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaArgumentWithoutType.kt")
|
||||
public void testLambdaArgumentWithoutType() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
|
||||
|
||||
js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java
Generated
+5
@@ -13597,6 +13597,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt19100.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt44804.kt")
|
||||
public void testKt44804() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/kt44804.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("multipleSmartCast.kt")
|
||||
public void testMultipleSmartCast() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/smartCasts/multipleSmartCast.kt");
|
||||
|
||||
Reference in New Issue
Block a user