[FIR] Support PreliminaryLoopVisitor in FIR DFA

This commit is contained in:
Dmitriy Novozhilov
2021-02-11 17:42:30 +03:00
parent 0e46a961a3
commit 5711a8d610
34 changed files with 294 additions and 106 deletions
@@ -37516,6 +37516,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt"); 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 @Test
@TestMetadata("lambdaArgumentWithoutType.kt") @TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception { public void testLambdaArgumentWithoutType() throws Exception {
@@ -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.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.CallableId import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.StandardClassIds 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.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.types.ConstantValueKind import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import org.jetbrains.kotlin.utils.addToStdlib.runIf
class DataFlowAnalyzerContext<FLOW : Flow>( class DataFlowAnalyzerContext<FLOW : Flow>(
val graphBuilder: ControlFlowGraphBuilder, val graphBuilder: ControlFlowGraphBuilder,
variableStorage: VariableStorage, variableStorage: VariableStorage,
flowOnNodes: MutableMap<CFGNode<*>, FLOW>, flowOnNodes: MutableMap<CFGNode<*>, FLOW>,
val variablesForWhenConditions: MutableMap<WhenBranchConditionExitNode, DataFlowVariable> val variablesForWhenConditions: MutableMap<WhenBranchConditionExitNode, DataFlowVariable>,
val preliminaryLoopVisitor: PreliminaryLoopVisitor
) { ) {
var flowOnNodes = flowOnNodes var flowOnNodes = flowOnNodes
private set private set
@@ -54,13 +55,15 @@ class DataFlowAnalyzerContext<FLOW : Flow>(
variableStorage = variableStorage.clear() variableStorage = variableStorage.clear()
flowOnNodes = mutableMapOf() flowOnNodes = mutableMapOf()
preliminaryLoopVisitor.resetState()
} }
companion object { companion object {
fun <FLOW : Flow> empty(session: FirSession) = fun <FLOW : Flow> empty(session: FirSession): DataFlowAnalyzerContext<FLOW> =
DataFlowAnalyzerContext<FLOW>( DataFlowAnalyzerContext<FLOW>(
ControlFlowGraphBuilder(), VariableStorage(session), ControlFlowGraphBuilder(), VariableStorage(session),
mutableMapOf(), mutableMapOf() mutableMapOf(), mutableMapOf(), PreliminaryLoopVisitor()
) )
} }
} }
@@ -210,12 +213,14 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
// TODO: questionable // TODO: questionable
postponedLambdaExitNode?.mergeIncomingFlow() postponedLambdaExitNode?.mergeIncomingFlow()
functionExitNode.mergeIncomingFlow() functionExitNode.mergeIncomingFlow()
exitCapturingStatement(anonymousFunction)
return FirControlFlowGraphReferenceImpl(graph) return FirControlFlowGraphReferenceImpl(graph)
} }
fun visitPostponedAnonymousFunction(anonymousFunction: FirAnonymousFunction) { fun visitPostponedAnonymousFunction(anonymousFunction: FirAnonymousFunction) {
val (enterNode, exitNode) = graphBuilder.visitPostponedAnonymousFunction(anonymousFunction) val (enterNode, exitNode) = graphBuilder.visitPostponedAnonymousFunction(anonymousFunction)
enterNode.mergeIncomingFlow() enterNode.mergeIncomingFlow()
enterCapturingStatement(enterNode, anonymousFunction)
exitNode.mergeIncomingFlow() exitNode.mergeIncomingFlow()
enterNode.flow = enterNode.flow.fork() enterNode.flow = enterNode.flow.fork()
} }
@@ -236,12 +241,14 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
private fun exitLocalClass(klass: FirRegularClass): ControlFlowGraph { private fun exitLocalClass(klass: FirRegularClass): ControlFlowGraph {
// TODO: support capturing of mutable properties, KT-44877
val (node, controlFlowGraph) = graphBuilder.exitLocalClass(klass) val (node, controlFlowGraph) = graphBuilder.exitLocalClass(klass)
node.mergeIncomingFlow() node.mergeIncomingFlow()
return controlFlowGraph return controlFlowGraph
} }
fun exitAnonymousObject(anonymousObject: FirAnonymousObject): ControlFlowGraph { fun exitAnonymousObject(anonymousObject: FirAnonymousObject): ControlFlowGraph {
// TODO: support capturing of mutable properties, KT-44877
val (node, controlFlowGraph) = graphBuilder.exitAnonymousObject(anonymousObject) val (node, controlFlowGraph) = graphBuilder.exitAnonymousObject(anonymousObject)
node.mergeIncomingFlow() node.mergeIncomingFlow()
return controlFlowGraph return controlFlowGraph
@@ -601,11 +608,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
shouldRemoveSynthetics = true shouldRemoveSynthetics = true
) )
} }
exitCapturingStatement(exitNode.fir)
} }
fun enterWhileLoop(loop: FirLoop) { fun enterWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop) val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
loopEnterNode.mergeIncomingFlow() loopEnterNode.mergeIncomingFlow()
enterCapturingStatement(loopEnterNode, loop)
loopConditionEnterNode.mergeIncomingFlow() loopConditionEnterNode.mergeIncomingFlow()
} }
@@ -630,11 +639,30 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
exitCommonLoop(exitNode) 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 ----------------------------------- // ----------------------------------- Do while Loop -----------------------------------
fun enterDoWhileLoop(loop: FirLoop) { fun enterDoWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopBlockEnterNode) = graphBuilder.enterDoWhileLoop(loop) val (loopEnterNode, loopBlockEnterNode) = graphBuilder.enterDoWhileLoop(loop)
loopEnterNode.mergeIncomingFlow() loopEnterNode.mergeIncomingFlow()
enterCapturingStatement(loopEnterNode, loop)
loopBlockEnterNode.mergeIncomingFlow() loopBlockEnterNode.mergeIncomingFlow()
} }
@@ -36,6 +36,7 @@ abstract class LogicSystem<FLOW : Flow>(protected val context: ConeInferenceCont
abstract fun addImplication(flow: FLOW, implication: Implication) abstract fun addImplication(flow: FLOW, implication: Implication)
abstract fun removeAllAboutVariable(flow: FLOW, variable: RealVariable) abstract fun removeAllAboutVariable(flow: FLOW, variable: RealVariable)
abstract fun removeAllAboutVariableIncludingAliasInformation(flow: FLOW, variable: RealVariable)
abstract fun translateVariableFromConditionInStatements( abstract fun translateVariableFromConditionInStatements(
flow: FLOW, flow: FLOW,
@@ -254,6 +254,21 @@ abstract class PersistentLogicSystem(context: ConeInferenceContext) : LogicSyste
// TODO: should we search variable in all logic statements? // 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( override fun translateVariableFromConditionInStatements(
flow: PersistentFlow, flow: PersistentFlow,
originalVariable: DataFlowVariable, originalVariable: DataFlowVariable,
@@ -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) @OptIn(DfaInternals::class)
class VariableStorage(private val session: FirSession) { class VariableStorage(private val session: FirSession) {
private var counter = 1 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() private val syntheticVariables: MutableMap<FirElement, SyntheticVariable> = HashMap()
fun clear(): VariableStorage = VariableStorage(session) fun clear(): VariableStorage = VariableStorage(session)
@@ -34,7 +37,7 @@ class VariableStorage(private val session: FirSession) {
fun getOrCreateRealVariableWithoutUnwrappingAlias(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable { fun getOrCreateRealVariableWithoutUnwrappingAlias(flow: Flow, symbol: AbstractFirBasedSymbol<*>, fir: FirElement): RealVariable {
val realFir = fir.unwrapElement() val realFir = fir.unwrapElement()
val identifier = getIdentifierBySymbol(flow, symbol, realFir) 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 { 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? { fun getRealVariableWithoutUnwrappingAlias(symbol: AbstractFirBasedSymbol<*>?, fir: FirElement, flow: Flow): RealVariable? {
val realFir = fir.unwrapElement() val realFir = fir.unwrapElement()
return symbol.takeIf { it.isStable(realFir) }?.let { 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<*>) { fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) {
realVariables.remove(Identifier(symbol, null, null)) _realVariables.remove(Identifier(symbol, null, null))
} }
fun removeSyntheticVariable(variable: DataFlowVariable) { fun removeSyntheticVariable(variable: DataFlowVariable) {
+1 -2
View File
@@ -1,6 +1,5 @@
// TARGET_BACKEND: JVM // TARGET_BACKEND: JVM
// WITH_RUNTIME // WITH_RUNTIME
// IGNORE_BACKEND_FIR: JVM_IR
open class A { open class A {
fun Foo() { fun Foo() {
@@ -23,4 +22,4 @@ fun box(): String {
test.Foo() test.Foo()
return "OK" return "OK"
} }
+43
View File
@@ -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 // Write to x is AFTER
x.hashCode() x.hashCode()
// No smart cast should be here! // No smart cast should be here!
foo(bar { x = null }, x.hashCode()) foo(bar { x = null }, x<!UNSAFE_CALL!>.<!>hashCode())
} }
@@ -4,10 +4,10 @@ fun foo(arg: Int?) {
if (x == null) return if (x == null) return
run { run {
// Unsafe because of owner modification // Unsafe because of owner modification
x.hashCode() x<!UNSAFE_CALL!>.<!>hashCode()
x = null x = null
} }
if (x != null) x = 42 if (x != null) x = 42
// Unsafe because of lambda // Unsafe because of lambda
x<!UNSAFE_CALL!>.<!>hashCode() x<!UNSAFE_CALL!>.<!>hashCode()
} }
@@ -6,5 +6,5 @@ fun foo(x: Int, f: () -> Unit, y: Int) {}
fun bar() { fun bar() {
var x: Int? var x: Int?
x = 4 x = 4
foo(x, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, x) <!INAPPLICABLE_CANDIDATE!>foo<!>(x, { x = null; x<!UNSAFE_CALL!>.<!>hashCode() }, x)
} }
@@ -2,8 +2,8 @@ public fun foo() {
var i: Any = 1 var i: Any = 1
if (i is Int) { if (i is Int) {
while (i != 10) { 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 = "" i = ""
} }
} }
} }
@@ -2,7 +2,7 @@ public fun foo() {
var i: Any = 1 var i: Any = 1
if (i is Int) { if (i is Int) {
while (i != 10) { while (i != 10) {
i++ i<!UNRESOLVED_REFERENCE!>++<!>
} }
} }
} }
@@ -13,5 +13,5 @@ fun list(start: String) {
e = e.next() e = e.next()
} }
// e can never be null but we do not know it // e can never be null but we do not know it
e.hashCode() e<!UNSAFE_CALL!>.<!>hashCode()
} }
@@ -13,7 +13,7 @@ fun foo(): Bar {
y = Bar() y = Bar()
while (x != null) { while (x != null) {
// Here call is unsafe because of inner loop // Here call is unsafe because of inner loop
y.next() y<!UNSAFE_CALL!>.<!>next()
while (y != null) { while (y != null) {
if (x == y) if (x == y)
// x is not null because of outer while // x is not null because of outer while
@@ -25,4 +25,4 @@ fun foo(): Bar {
x = x.next() x = x.next()
} }
return Bar() return Bar()
} }
@@ -30,7 +30,7 @@ fun baz(s: String?) {
x.hashCode() x.hashCode()
} }
run { run {
x.hashCode() x<!UNSAFE_CALL!>.<!>hashCode()
x = null x = null
} }
} }
@@ -40,11 +40,11 @@ fun gaz(s: String?) {
var x = s var x = s
if (x != null) { if (x != null) {
run { run {
x.hashCode() x<!UNSAFE_CALL!>.<!>hashCode()
x = null x = null
} }
run { run {
x.hashCode() x<!UNSAFE_CALL!>.<!>hashCode()
} }
} }
} }
@@ -57,4 +57,4 @@ fun gav(s: String?) {
} }
x = null x = null
} }
} }
@@ -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?) { data class SomeObject(val n: SomeObject?) {
fun doSomething(): Boolean = true fun doSomething(): Boolean = true
fun next(): SomeObject? = n fun next(): SomeObject? = n
@@ -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,3 +1,4 @@
// FIR_IDENTICAL
fun x(): Boolean { return true } fun x(): Boolean { return true }
public fun foo(pp: String?): Int { public fun foo(pp: String?): Int {
@@ -9,9 +9,9 @@ fun list(start: SomeObject): SomeObject {
var e: SomeObject? = start var e: SomeObject? = start
for (i in 0..42) { for (i in 0..42) {
// Unsafe calls because of nullable e at the beginning // Unsafe calls because of nullable e at the beginning
e.doSomething() e<!UNSAFE_CALL!>.<!>doSomething()
e = e.next() e = e<!UNSAFE_CALL!>.<!>next()
} }
// Smart cast is not possible here due to next() // Smart cast is not possible here due to next()
return e return e
} }
@@ -16,5 +16,5 @@ fun list(start: SomeObject) {
e = e.next() e = e.next()
} }
// e can be null because of next() // e can be null because of next()
e.doSomething() e<!UNSAFE_CALL!>.<!>doSomething()
} }
@@ -2,8 +2,8 @@ public fun foo() {
var i: Int? = 1 var i: Int? = 1
if (i != null) { if (i != null) {
while (i != 10) { 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 i = null
} }
} }
} }
@@ -2,7 +2,7 @@ public fun foo() {
var i: Int? = 1 var i: Int? = 1
if (i != null) { if (i != null) {
while (i != 10) { while (i != 10) {
i++ i<!UNSAFE_CALL!>++<!>
} }
} }
} }
@@ -13,5 +13,5 @@ fun list(start: SomeObject) {
e = e.next() e = e.next()
} }
// e can be null because of next() // e can be null because of next()
e.doSomething() e<!UNSAFE_CALL!>.<!>doSomething()
} }
@@ -37516,6 +37516,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt"); 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 @Test
@TestMetadata("lambdaArgumentWithoutType.kt") @TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception { public void testLambdaArgumentWithoutType() throws Exception {
@@ -37516,6 +37516,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt"); 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 @Test
@TestMetadata("lambdaArgumentWithoutType.kt") @TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception { public void testLambdaArgumentWithoutType() throws Exception {
@@ -29984,6 +29984,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt"); 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") @TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception { public void testLambdaArgumentWithoutType() throws Exception {
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt"); 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<!>.length
} }
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length <!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<!>.length
} }
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length <!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<!>.length
} }
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length <!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<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
while (if (true) { b = a; true } else true) { while (if (true) { b = a; true } else true) {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
} }
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length <!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 <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length
while (true) { while (true) {
if (true) { b = a; } else 3 if (true) { b = a; } else 3
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String & kotlin.String")!>b<!>.length <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.String")!>b<!>.length
} }
} }
} }
@@ -11,8 +11,8 @@ fun case_1() {
var x: Any? = null var x: Any? = null
if (x == null) return if (x == null) return
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x != null) } while (x != null)
} }
@@ -25,8 +25,8 @@ fun case_2() {
var x: Any? = null var x: Any? = null
if (x === null) return if (x === null) return
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x !== null) } while (x !== null)
} }
@@ -57,8 +57,8 @@ fun case_5() {
var x: Any? = null var x: Any? = null
if (x == null) return if (x == null) return
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x !== null) } while (x !== null)
} }
@@ -71,8 +71,8 @@ fun case_6() {
var x: Any? = null var x: Any? = null
if (x === null) return if (x === null) return
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x != null) } while (x != null)
} }
@@ -85,8 +85,8 @@ fun case_7() {
var x: Any? = null var x: Any? = null
x ?: return x ?: return
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x !== null) } while (x !== null)
} }
@@ -99,8 +99,8 @@ fun case_8() {
var x: Any? = null var x: Any? = null
if (x == null) throw Exception() if (x == null) throw Exception()
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x != null) } while (x != null)
} }
@@ -113,8 +113,8 @@ fun case_9() {
var x: Any? = null var x: Any? = null
x ?: throw Exception() x ?: throw Exception()
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x !== null) } while (x !== null)
} }
@@ -127,8 +127,8 @@ fun case_10() {
var x: Any? = null var x: Any? = null
x as Any x as Any
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x != null) } while (x != null)
} }
@@ -155,8 +155,8 @@ fun case_12() {
var x: Any? = null var x: Any? = null
if (x is Any) { if (x is Any) {
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x .equals(10) x = x <!UNSAFE_CALL!>.<!>equals(10)
} while (x != null) } while (x != null)
} }
} }
@@ -170,8 +170,8 @@ fun case_13() {
var x: Any? = null var x: Any? = null
if (x != null) { if (x != null) {
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x .equals(10) x = x <!UNSAFE_CALL!>.<!>equals(10)
} while (x != null) } while (x != null)
} }
} }
@@ -185,8 +185,8 @@ fun case_14() {
var x: Any? = null var x: Any? = null
if (x == null) return if (x == null) return
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x.equals(10) x = x<!UNSAFE_CALL!>.<!>equals(10)
} while (x is Any) } while (x is Any)
} }
@@ -199,8 +199,8 @@ fun case_15() {
var x: Any? = null var x: Any? = null
if (x is Any) { if (x is Any) {
do { do {
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any & kotlin.Any?")!>x<!> <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Any?")!>x<!>
x = x .equals(10) x = x <!UNSAFE_CALL!>.<!>equals(10)
} while (x is Any) } while (x is Any)
} }
} }
@@ -25530,6 +25530,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt"); 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") @TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception { public void testLambdaArgumentWithoutType() throws Exception {
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt"); runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
@@ -25015,6 +25015,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt"); 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") @TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception { public void testLambdaArgumentWithoutType() throws Exception {
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt"); runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
@@ -24975,6 +24975,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/smartCasts/kt42517.kt"); 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") @TestMetadata("lambdaArgumentWithoutType.kt")
public void testLambdaArgumentWithoutType() throws Exception { public void testLambdaArgumentWithoutType() throws Exception {
runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt"); runTest("compiler/testData/codegen/box/smartCasts/lambdaArgumentWithoutType.kt");
@@ -13597,6 +13597,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/smartCasts/kt19100.kt"); 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") @TestMetadata("multipleSmartCast.kt")
public void testMultipleSmartCast() throws Exception { public void testMultipleSmartCast() throws Exception {
runTest("compiler/testData/codegen/box/smartCasts/multipleSmartCast.kt"); runTest("compiler/testData/codegen/box/smartCasts/multipleSmartCast.kt");