FIR: Support FirComparisonOperator in body transformers and DFA

^KT-31163 In Progress
This commit is contained in:
Denis Zharkov
2020-02-26 16:45:57 +03:00
parent c3aed433ae
commit 434444cd69
31 changed files with 191 additions and 149 deletions
@@ -233,6 +233,10 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
node.flow = flow node.flow = flow
} }
fun exitComparisonExpressionCall(comparisonExpression: FirComparisonExpression) {
graphBuilder.exitComparisonExpression(comparisonExpression).mergeIncomingFlow()
}
fun exitOperatorCall(operatorCall: FirOperatorCall) { fun exitOperatorCall(operatorCall: FirOperatorCall) {
val node = graphBuilder.exitOperatorCall(operatorCall).mergeIncomingFlow() val node = graphBuilder.exitOperatorCall(operatorCall).mergeIncomingFlow()
when (val operation = operatorCall.operation) { when (val operation = operatorCall.operation) {
@@ -180,6 +180,7 @@ class BinaryOrExitNode(owner: ControlFlowGraph, override val fir: FirBinaryLogic
class TypeOperatorCallNode(owner: ControlFlowGraph, override val fir: FirTypeOperatorCall, level: Int, id: Int) : CFGNode<FirTypeOperatorCall>(owner, level, id) class TypeOperatorCallNode(owner: ControlFlowGraph, override val fir: FirTypeOperatorCall, level: Int, id: Int) : CFGNode<FirTypeOperatorCall>(owner, level, id)
class OperatorCallNode(owner: ControlFlowGraph, override val fir: FirOperatorCall, level: Int, id: Int) : AbstractBinaryExitNode<FirOperatorCall>(owner, level, id) class OperatorCallNode(owner: ControlFlowGraph, override val fir: FirOperatorCall, level: Int, id: Int) : AbstractBinaryExitNode<FirOperatorCall>(owner, level, id)
class ComparisonExpressionNode(owner: ControlFlowGraph, override val fir: FirComparisonExpression, level: Int, id: Int) : CFGNode<FirComparisonExpression>(owner, level, id)
// ----------------------------------- Jump ----------------------------------- // ----------------------------------- Jump -----------------------------------
@@ -256,6 +256,10 @@ class ControlFlowGraphBuilder {
return createOperatorCallNode(operatorCall).also { addNewSimpleNode(it) } return createOperatorCallNode(operatorCall).also { addNewSimpleNode(it) }
} }
fun exitComparisonExpression(comparisonExpression: FirComparisonExpression): ComparisonExpressionNode {
return ComparisonExpressionNode(graph, comparisonExpression, levelCounter, createId()).also { addNewSimpleNode(it) }
}
// ----------------------------------- Jump ----------------------------------- // ----------------------------------- Jump -----------------------------------
fun exitJump(jump: FirJump<*>): JumpNode { fun exitJump(jump: FirJump<*>): JumpNode {
@@ -723,4 +727,4 @@ class ControlFlowGraphBuilder {
fun reset() { fun reset() {
exitsOfAnonymousFunctions.clear() exitsOfAnonymousFunctions.clear()
} }
} }
@@ -159,6 +159,7 @@ private fun CFGNode<*>.render(): String =
is QualifiedAccessNode -> "Access variable ${fir.calleeReference.render(FirRenderer.RenderMode.DontRenderLambdaBodies)}" is QualifiedAccessNode -> "Access variable ${fir.calleeReference.render(FirRenderer.RenderMode.DontRenderLambdaBodies)}"
is OperatorCallNode -> "Operator ${fir.operation.operator}" is OperatorCallNode -> "Operator ${fir.operation.operator}"
is ComparisonExpressionNode -> "Comparison ${fir.operation.operator}"
is TypeOperatorCallNode -> "Type operator: \"${fir.render(FirRenderer.RenderMode.DontRenderLambdaBodies)}\"" is TypeOperatorCallNode -> "Type operator: \"${fir.render(FirRenderer.RenderMode.DontRenderLambdaBodies)}\""
is JumpNode -> "Jump: ${fir.render()}" is JumpNode -> "Jump: ${fir.render()}"
is StubNode -> "Stub" is StubNode -> "Stub"
@@ -244,4 +245,4 @@ private fun ControlFlowGraph.sortNodes(): List<CFGNode<*>> {
} }
} }
private fun List<CFGNode<*>>.indicesMap(): Map<CFGNode<*>, Int> = mapIndexed { i, node -> node to i }.toMap() private fun List<CFGNode<*>>.indicesMap(): Map<CFGNode<*>, Int> = mapIndexed { i, node -> node to i }.toMap()
@@ -106,6 +106,13 @@ open class FirBodyResolveTransformer(
return expressionsTransformer.transformThisReceiverExpression(thisReceiverExpression, data) return expressionsTransformer.transformThisReceiverExpression(thisReceiverExpression, data)
} }
override fun transformComparisonExpression(
comparisonExpression: FirComparisonExpression,
data: ResolutionMode
): CompositeTransformResult<FirStatement> {
return expressionsTransformer.transformComparisonExpression(comparisonExpression, data)
}
override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ResolutionMode): CompositeTransformResult<FirStatement> { override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ResolutionMode): CompositeTransformResult<FirStatement> {
return expressionsTransformer.transformOperatorCall(operatorCall, data) return expressionsTransformer.transformOperatorCall(operatorCall, data)
} }
@@ -208,6 +208,15 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
return transformQualifiedAccessExpression(thisReceiverExpression, data) return transformQualifiedAccessExpression(thisReceiverExpression, data)
} }
override fun transformComparisonExpression(
comparisonExpression: FirComparisonExpression,
data: ResolutionMode
): CompositeTransformResult<FirStatement> {
return (comparisonExpression.transformChildren(transformer, ResolutionMode.ContextIndependent) as FirComparisonExpression).also {
it.resultType = comparisonExpression.typeRef.resolvedTypeFromPrototype(builtinTypes.booleanType.type)
}.transformSingle(integerLiteralTypeApproximator, null).also(dataFlowAnalyzer::exitComparisonExpressionCall).compose()
}
override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ResolutionMode): CompositeTransformResult<FirStatement> { override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ResolutionMode): CompositeTransformResult<FirStatement> {
if (operatorCall.operation in FirOperation.BOOLEANS) { if (operatorCall.operation in FirOperation.BOOLEANS) {
// TODO: add approximation of integer literals // TODO: add approximation of integer literals
+1 -1
View File
@@ -1,7 +1,7 @@
FILE: fib.kt FILE: fib.kt
public final fun fibIterative(n: R|kotlin/Int|): R|kotlin/Int| { public final fun fibIterative(n: R|kotlin/Int|): R|kotlin/Int| {
when () { when () {
<(R|<local>/n|, Int(2)) -> { CMP(<, R|<local>/n|.R|kotlin/Int.compareTo|(Int(2))) -> {
^fibIterative Int(1) ^fibIterative Int(1)
} }
} }
@@ -7,15 +7,15 @@ FILE: kotlinSam.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
R|/foo|(R|/MyRunnable|(<L> = MyRunnable@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { R|/foo|(R|/MyRunnable|(<L> = MyRunnable@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
)) ))
R|/foo|(R|/MyRunnable|(MyRunnable@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| { R|/foo|(R|/MyRunnable|(MyRunnable@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/it|, Int(1)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(1)))
} }
)) ))
lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
R|/foo|(R|/MyRunnable|(R|<local>/x|)) R|/foo|(R|/MyRunnable|(R|<local>/x|))
@@ -6,15 +6,15 @@ FILE: main.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
<Inapplicable(INAPPLICABLE): [/foo]>#(R|/MyRunnable|(<L> = MyRunnable@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { <Inapplicable(INAPPLICABLE): [/foo]>#(R|/MyRunnable|(<L> = MyRunnable@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
)) ))
<Inapplicable(INAPPLICABLE): [/foo]>#(R|/MyRunnable|(MyRunnable@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| { <Inapplicable(INAPPLICABLE): [/foo]>#(R|/MyRunnable|(MyRunnable@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/it|, Int(1)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(1)))
} }
)) ))
lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
<Inapplicable(INAPPLICABLE): [/foo]>#(R|/MyRunnable|(R|<local>/x|)) <Inapplicable(INAPPLICABLE): [/foo]>#(R|/MyRunnable|(R|<local>/x|))
@@ -3,15 +3,15 @@ FILE: main.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
R|/foo|(R|/MyRunnable|(<L> = MyRunnable@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { R|/foo|(R|/MyRunnable|(<L> = MyRunnable@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
)) ))
R|/foo|(R|/MyRunnable|(MyRunnable@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| { R|/foo|(R|/MyRunnable|(MyRunnable@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/it|, Int(1)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(1)))
} }
)) ))
lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
R|/foo|(R|/MyRunnable|(R|<local>/x|)) R|/foo|(R|/MyRunnable|(R|<local>/x|))
@@ -30,10 +30,10 @@ fun main() {
foo1 { x -> x > 1 } foo1 { x -> x > 1 }
foo1(f) foo1(f)
<!INAPPLICABLE_CANDIDATE!>foo2<!> { x -> x > 1 } <!INAPPLICABLE_CANDIDATE!>foo2<!> { x -> x <!UNRESOLVED_REFERENCE!>><!> 1 }
<!INAPPLICABLE_CANDIDATE!>foo2<!>(f) <!INAPPLICABLE_CANDIDATE!>foo2<!>(f)
<!INAPPLICABLE_CANDIDATE!>foo3<!> { x -> x > 1 } <!INAPPLICABLE_CANDIDATE!>foo3<!> { x -> x <!UNRESOLVED_REFERENCE!>><!> 1 }
<!INAPPLICABLE_CANDIDATE!>foo3<!>(f) <!INAPPLICABLE_CANDIDATE!>foo3<!>(f)
foo4 { x -> x > 1 } foo4 { x -> x > 1 }
@@ -30,26 +30,26 @@ FILE: kotlinSam.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
lval f: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(t: R|kotlin/Int|): R|kotlin/Boolean| { lval f: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(t: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/t|, Int(1)) ^ CMP(>, R|<local>/t|.R|kotlin/Int.compareTo|(Int(1)))
} }
R|/foo1|(<L> = foo1@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { R|/foo1|(<L> = foo1@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
R|/foo1|(R|<local>/f|) R|/foo1|(R|<local>/f|)
<Inapplicable(INAPPLICABLE): [/foo2]>#(<L> = foo2@fun <anonymous>(x: R|ERROR CLASS: No type for parameter|): R|kotlin/Boolean| { <Inapplicable(INAPPLICABLE): [/foo2]>#(<L> = foo2@fun <anonymous>(x: R|ERROR CLASS: No type for parameter|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1)))
} }
) )
<Inapplicable(INAPPLICABLE): [/foo2]>#(R|<local>/f|) <Inapplicable(INAPPLICABLE): [/foo2]>#(R|<local>/f|)
<Inapplicable(INAPPLICABLE): [/foo3]>#(<L> = foo3@fun <anonymous>(x: R|ERROR CLASS: No type for parameter|): R|kotlin/Boolean| { <Inapplicable(INAPPLICABLE): [/foo3]>#(<L> = foo3@fun <anonymous>(x: R|ERROR CLASS: No type for parameter|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1)))
} }
) )
<Inapplicable(INAPPLICABLE): [/foo3]>#(R|<local>/f|) <Inapplicable(INAPPLICABLE): [/foo3]>#(R|<local>/f|)
R|/foo4|(<L> = foo4@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { R|/foo4|(<L> = foo4@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
R|/foo4|(R|<local>/f|) R|/foo4|(R|<local>/f|)
@@ -20,10 +20,10 @@ fun foo(m: MyRunnable) {}
fun main() { fun main() {
JavaUsage.<!INAPPLICABLE_CANDIDATE!>foo<!> { JavaUsage.<!INAPPLICABLE_CANDIDATE!>foo<!> {
x -> x ->
x > 1 x <!UNRESOLVED_REFERENCE!>><!> 1
} }
JavaUsage.<!INAPPLICABLE_CANDIDATE!>foo<!>({ <!UNRESOLVED_REFERENCE!>it<!> > 1 }) JavaUsage.<!INAPPLICABLE_CANDIDATE!>foo<!>({ <!UNRESOLVED_REFERENCE!>it<!> <!UNRESOLVED_REFERENCE!>><!> 1 })
val x = { x: Int -> x > 1 } val x = { x: Int -> x > 1 }
@@ -3,15 +3,15 @@ FILE: main.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
Q|JavaUsage|.<Inapplicable(INAPPLICABLE): [/JavaUsage.foo]>#(<L> = foo@fun <anonymous>(x: R|ERROR CLASS: No type for parameter|): R|kotlin/Boolean| { Q|JavaUsage|.<Inapplicable(INAPPLICABLE): [/JavaUsage.foo]>#(<L> = foo@fun <anonymous>(x: R|ERROR CLASS: No type for parameter|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.<Unresolved name: compareTo>#(Int(1)))
} }
) )
Q|JavaUsage|.<Inapplicable(INAPPLICABLE): [/JavaUsage.foo]>#(foo@fun <anonymous>(): R|kotlin/Boolean| { Q|JavaUsage|.<Inapplicable(INAPPLICABLE): [/JavaUsage.foo]>#(foo@fun <anonymous>(): R|kotlin/Boolean| {
^ >(<Unresolved name: it>#, Int(1)) ^ CMP(>, <Unresolved name: it>#.<Unresolved name: compareTo>#(Int(1)))
} }
) )
lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
Q|JavaUsage|.<Inapplicable(INAPPLICABLE): [/JavaUsage.foo]>#(R|<local>/x|) Q|JavaUsage|.<Inapplicable(INAPPLICABLE): [/JavaUsage.foo]>#(R|<local>/x|)
@@ -3,15 +3,15 @@ FILE: main.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
Q|JavaUsage|.R|/JavaUsage.foo|(<L> = foo@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { Q|JavaUsage|.R|/JavaUsage.foo|(<L> = foo@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| { Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/it|, Int(1)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
Q|JavaUsage|.R|/JavaUsage.foo|(R|<local>/x|) Q|JavaUsage|.R|/JavaUsage.foo|(R|<local>/x|)
@@ -3,15 +3,15 @@ FILE: main.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
Q|JavaUsage|.R|/JavaUsage.foo|(<L> = foo@fun <anonymous>(x: R|kotlin/Int|): R|ft<kotlin/Boolean, kotlin/Boolean?>!| { Q|JavaUsage|.R|/JavaUsage.foo|(<L> = foo@fun <anonymous>(x: R|kotlin/Int|): R|ft<kotlin/Boolean, kotlin/Boolean?>!| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun <anonymous>(it: R|kotlin/Int|): R|ft<kotlin/Boolean, kotlin/Boolean?>!| { Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun <anonymous>(it: R|kotlin/Int|): R|ft<kotlin/Boolean, kotlin/Boolean?>!| {
^ >(R|<local>/it|, Int(1)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
Q|JavaUsage|.R|/JavaUsage.foo|(R|<local>/x|) Q|JavaUsage|.R|/JavaUsage.foo|(R|<local>/x|)
@@ -3,15 +3,15 @@ FILE: main.kt
} }
public final fun main(): R|kotlin/Unit| { public final fun main(): R|kotlin/Unit| {
Q|JavaUsage|.R|/JavaUsage.foo|(<L> = foo@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { Q|JavaUsage|.R|/JavaUsage.foo|(<L> = foo@fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| { Q|JavaUsage|.R|/JavaUsage.foo|(foo@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/it|, Int(1)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(1)))
} }
) )
lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| { lval x: R|(kotlin/Int) -> kotlin/Boolean| = fun <anonymous>(x: R|kotlin/Int|): R|kotlin/Boolean| {
^ >(R|<local>/x|, Int(1)) ^ CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1)))
} }
Q|JavaUsage|.R|/JavaUsage.foo|(R|<local>/x|) Q|JavaUsage|.R|/JavaUsage.foo|(R|<local>/x|)
@@ -11,7 +11,7 @@ FILE: ifWithCR.kt
private set(value: R|kotlin/CharSequence?|): R|kotlin/Unit| private set(value: R|kotlin/CharSequence?|): R|kotlin/Unit|
public final fun main(x: R|kotlin/Int|): R|kotlin/Unit| { public final fun main(x: R|kotlin/Int|): R|kotlin/Unit| {
lval x: R|kotlin/reflect/KMutableProperty0<kotlin/CharSequence?>| = when () { lval x: R|kotlin/reflect/KMutableProperty0<kotlin/CharSequence?>| = when () {
>(R|<local>/x|, Int(1)) -> { CMP(>, R|<local>/x|.R|kotlin/Int.compareTo|(Int(1))) -> {
R|<local>/x|::R|/readOnlyWrapper| R|<local>/x|::R|/readOnlyWrapper|
} }
else -> { else -> {
@@ -100,183 +100,187 @@ digraph callsInPlace_kt {
34 [label="Const: String(test_4)"]; 34 [label="Const: String(test_4)"];
35 [label="Access variable R|<local>/it|"]; 35 [label="Access variable R|<local>/it|"];
36 [label="Const: Int(0)"]; 36 [label="Const: Int(0)"];
37 [label="Operator >"]; 37 [label="Function call: R|<local>/it|.R|kotlin/Int.compareTo|(Int(0))"];
38 [label="Exit function anonymousFunction"]; 38 [label="Comparison >"];
39 [label="Exit function anonymousFunction"];
} }
39 [label="Postponed exit from lambda"]; 40 [label="Postponed exit from lambda"];
40 [label="Function call: Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(<L> = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> )"]; 41 [label="Function call: Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(<L> = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> )"];
41 [label="Exit function test_4" style="filled" fillcolor=red]; 42 [label="Exit function test_4" style="filled" fillcolor=red];
} }
30 -> {31}; 30 -> {31};
31 -> {32}; 31 -> {32};
32 -> {33}; 32 -> {33};
32 -> {39} [color=red]; 32 -> {40} [color=red];
33 -> {34}; 33 -> {34};
34 -> {35}; 34 -> {35};
35 -> {36}; 35 -> {36};
36 -> {37}; 36 -> {37};
37 -> {38}; 37 -> {38};
38 -> {39} [color=green]; 38 -> {39};
39 -> {40}; 39 -> {40} [color=green];
40 -> {41}; 40 -> {41};
41 -> {42};
subgraph cluster_8 { subgraph cluster_8 {
color=red color=red
42 [label="Enter function test_5" style="filled" fillcolor=red]; 43 [label="Enter function test_5" style="filled" fillcolor=red];
43 [label="Const: Int(1)"]; 44 [label="Const: Int(1)"];
44 [label="Postponed enter to lambda"]; 45 [label="Postponed enter to lambda"];
subgraph cluster_9 { subgraph cluster_9 {
color=blue color=blue
45 [label="Enter function anonymousFunction"]; 46 [label="Enter function anonymousFunction"];
46 [label="Const: String(test_5)"]; 47 [label="Const: String(test_5)"];
47 [label="Access variable R|<local>/it|"]; 48 [label="Access variable R|<local>/it|"];
48 [label="Const: Int(0)"]; 49 [label="Const: Int(0)"];
49 [label="Operator >"]; 50 [label="Function call: R|<local>/it|.R|kotlin/Int.compareTo|(Int(0))"];
50 [label="Exit function anonymousFunction"]; 51 [label="Comparison >"];
52 [label="Exit function anonymousFunction"];
} }
51 [label="Postponed exit from lambda"]; 53 [label="Postponed exit from lambda"];
52 [label="Function call: Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(predicate = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> )"]; 54 [label="Function call: Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(predicate = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> )"];
53 [label="Exit function test_5" style="filled" fillcolor=red]; 55 [label="Exit function test_5" style="filled" fillcolor=red];
} }
42 -> {43};
43 -> {44}; 43 -> {44};
44 -> {45}; 44 -> {45};
44 -> {51} [color=red];
45 -> {46}; 45 -> {46};
45 -> {53} [color=red];
46 -> {47}; 46 -> {47};
47 -> {48}; 47 -> {48};
48 -> {49}; 48 -> {49};
49 -> {50}; 49 -> {50};
50 -> {51} [color=green]; 50 -> {51};
51 -> {52}; 51 -> {52};
52 -> {53}; 52 -> {53} [color=green];
53 -> {54};
54 -> {55};
subgraph cluster_10 { subgraph cluster_10 {
color=red color=red
54 [label="Enter function myRun" style="filled" fillcolor=red]; 56 [label="Enter function myRun" style="filled" fillcolor=red];
55 [label="Function call: R|<local>/block1|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"]; 57 [label="Function call: R|<local>/block1|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
56 [label="Function call: R|<local>/block2|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"]; 58 [label="Function call: R|<local>/block2|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
57 [label="Exit function myRun" style="filled" fillcolor=red]; 59 [label="Exit function myRun" style="filled" fillcolor=red];
} }
54 -> {55};
55 -> {56};
56 -> {57}; 56 -> {57};
57 -> {58};
58 -> {59};
subgraph cluster_11 { subgraph cluster_11 {
color=red color=red
58 [label="Enter function test_6" style="filled" fillcolor=red]; 60 [label="Enter function test_6" style="filled" fillcolor=red];
59 [label="Postponed enter to lambda"]; 61 [label="Postponed enter to lambda"];
subgraph cluster_12 { subgraph cluster_12 {
color=blue color=blue
60 [label="Enter function anonymousFunction"]; 62 [label="Enter function anonymousFunction"];
61 [label="Const: String(test_6_1)"]; 63 [label="Const: String(test_6_1)"];
62 [label="Exit function anonymousFunction"]; 64 [label="Exit function anonymousFunction"];
} }
63 [label="Postponed exit from lambda"]; 65 [label="Postponed exit from lambda"];
64 [label="Postponed enter to lambda"]; 66 [label="Postponed enter to lambda"];
subgraph cluster_13 { subgraph cluster_13 {
color=blue color=blue
65 [label="Enter function anonymousFunction"]; 67 [label="Enter function anonymousFunction"];
66 [label="Const: String(test_6_2)"]; 68 [label="Const: String(test_6_2)"];
67 [label="Exit function anonymousFunction"]; 69 [label="Exit function anonymousFunction"];
} }
68 [label="Postponed exit from lambda"]; 70 [label="Postponed exit from lambda"];
69 [label="Function call: R|/myRun|(myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> , <L> = myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> )"]; 71 [label="Function call: R|/myRun|(myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> , <L> = myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> )"];
70 [label="Exit function test_6" style="filled" fillcolor=red]; 72 [label="Exit function test_6" style="filled" fillcolor=red];
} }
58 -> {59}; 60 -> {61};
59 -> {60};
59 -> {63} [color=red];
60 -> {62 61};
61 -> {62}; 61 -> {62};
62 -> {60}; 61 -> {65} [color=red];
62 -> {63} [color=green]; 62 -> {64 63};
63 -> {64}; 63 -> {64};
64 -> {65}; 64 -> {62};
64 -> {68} [color=red]; 64 -> {65} [color=green];
65 -> {67 66}; 65 -> {66};
66 -> {67}; 66 -> {67};
67 -> {65}; 66 -> {70} [color=red];
67 -> {68} [color=green]; 67 -> {69 68};
68 -> {69}; 68 -> {69};
69 -> {70}; 69 -> {67};
69 -> {70} [color=green];
70 -> {71};
71 -> {72};
subgraph cluster_14 { subgraph cluster_14 {
color=red color=red
71 [label="Enter function test_7" style="filled" fillcolor=red]; 73 [label="Enter function test_7" style="filled" fillcolor=red];
72 [label="Postponed enter to lambda"]; 74 [label="Postponed enter to lambda"];
subgraph cluster_15 { subgraph cluster_15 {
color=blue color=blue
73 [label="Enter function anonymousFunction"]; 75 [label="Enter function anonymousFunction"];
74 [label="Const: String(test_7_2)"]; 76 [label="Const: String(test_7_2)"];
75 [label="Exit function anonymousFunction"]; 77 [label="Exit function anonymousFunction"];
} }
76 [label="Postponed exit from lambda"]; 78 [label="Postponed exit from lambda"];
77 [label="Postponed enter to lambda"]; 79 [label="Postponed enter to lambda"];
subgraph cluster_16 { subgraph cluster_16 {
color=blue color=blue
78 [label="Enter function anonymousFunction"]; 80 [label="Enter function anonymousFunction"];
79 [label="Const: String(test_7_1)"]; 81 [label="Const: String(test_7_1)"];
80 [label="Exit function anonymousFunction"]; 82 [label="Exit function anonymousFunction"];
} }
81 [label="Postponed exit from lambda"]; 83 [label="Postponed exit from lambda"];
82 [label="Function call: R|/myRun|(block2 = myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> , block1 = myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> )"]; 84 [label="Function call: R|/myRun|(block2 = myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> , block1 = myRun@fun <anonymous>(): R|kotlin/Unit| <kind=UNKNOWN> )"];
83 [label="Exit function test_7" style="filled" fillcolor=red]; 85 [label="Exit function test_7" style="filled" fillcolor=red];
} }
71 -> {72}; 73 -> {74};
72 -> {73};
72 -> {76} [color=red];
73 -> {75 74};
74 -> {75}; 74 -> {75};
75 -> {73}; 74 -> {78} [color=red];
75 -> {76} [color=green]; 75 -> {77 76};
76 -> {77}; 76 -> {77};
77 -> {78}; 77 -> {75};
77 -> {81} [color=red]; 77 -> {78} [color=green];
78 -> {80 79}; 78 -> {79};
79 -> {80}; 79 -> {80};
80 -> {78}; 79 -> {83} [color=red];
80 -> {81} [color=green]; 80 -> {82 81};
81 -> {82}; 81 -> {82};
82 -> {83}; 82 -> {80};
82 -> {83} [color=green];
83 -> {84};
84 -> {85};
subgraph cluster_17 { subgraph cluster_17 {
color=red color=red
84 [label="Enter function myDummyRun" style="filled" fillcolor=red]; 86 [label="Enter function myDummyRun" style="filled" fillcolor=red];
85 [label="Function call: R|<local>/block|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"]; 87 [label="Function call: R|<local>/block|.R|FakeOverride<kotlin/Function0.invoke: R|kotlin/Unit|>|()"];
86 [label="Exit function myDummyRun" style="filled" fillcolor=red]; 88 [label="Exit function myDummyRun" style="filled" fillcolor=red];
} }
84 -> {85}; 86 -> {87};
85 -> {86}; 87 -> {88};
subgraph cluster_18 { subgraph cluster_18 {
color=red color=red
87 [label="Enter function test_8" style="filled" fillcolor=red]; 89 [label="Enter function test_8" style="filled" fillcolor=red];
88 [label="Postponed enter to lambda"]; 90 [label="Postponed enter to lambda"];
89 [label="Postponed exit from lambda"]; 91 [label="Postponed exit from lambda"];
90 [label="Function call: R|/myDummyRun|(<L> = myDummyRun@fun <anonymous>(): R|kotlin/Unit|)"]; 92 [label="Function call: R|/myDummyRun|(<L> = myDummyRun@fun <anonymous>(): R|kotlin/Unit|)"];
91 [label="Exit function test_8" style="filled" fillcolor=red]; 93 [label="Exit function test_8" style="filled" fillcolor=red];
} }
87 -> {88};
88 -> {89 89} [color=green];
89 -> {90}; 89 -> {90};
90 -> {91}; 90 -> {91 91} [color=green];
91 -> {92};
92 -> {93};
subgraph cluster_19 { subgraph cluster_19 {
color=red color=red
92 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; 94 [label="Enter function anonymousFunction" style="filled" fillcolor=red];
93 [label="Const: String(test_8)"]; 95 [label="Const: String(test_8)"];
94 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; 96 [label="Exit function anonymousFunction" style="filled" fillcolor=red];
} }
92 -> {93}; 94 -> {95};
93 -> {94}; 95 -> {96};
} }
@@ -22,14 +22,14 @@ FILE: callsInPlace.kt
public final fun test_4(): R|kotlin/Unit| { public final fun test_4(): R|kotlin/Unit| {
Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(<L> = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> { Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(<L> = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> {
String(test_4) String(test_4)
^ >(R|<local>/it|, Int(0)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(0)))
} }
) )
} }
public final fun test_5(): R|kotlin/Unit| { public final fun test_5(): R|kotlin/Unit| {
Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(predicate = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> { Int(1).R|kotlin/takeUnless|<R|kotlin/Int|>(predicate = takeUnless@fun <anonymous>(it: R|kotlin/Int|): R|kotlin/Boolean| <kind=EXACTLY_ONCE> {
String(test_5) String(test_5)
^ >(R|<local>/it|, Int(0)) ^ CMP(>, R|<local>/it|.R|kotlin/Int.compareTo|(Int(0)))
} }
) )
} }
@@ -1020,6 +1020,12 @@ class FirRenderer(builder: StringBuilder, private val mode: RenderMode = RenderM
visitCall(operatorCall) visitCall(operatorCall)
} }
override fun visitComparisonExpression(comparisonExpression: FirComparisonExpression) {
print("CMP(${comparisonExpression.operation.operator}, ")
comparisonExpression.compareToCall.accept(this)
print(")")
}
override fun visitComponentCall(componentCall: FirComponentCall) { override fun visitComponentCall(componentCall: FirComponentCall) {
componentCall.annotations.renderAnnotations() componentCall.annotations.renderAnnotations()
componentCall.explicitReceiver.accept(this) componentCall.explicitReceiver.accept(this)
@@ -8,7 +8,7 @@ fun f(): Unit {
x = null x = null
x <!INAPPLICABLE_CANDIDATE!>+<!> 1 x <!INAPPLICABLE_CANDIDATE!>+<!> 1
x <!INAPPLICABLE_CANDIDATE!>plus<!> 1 x <!INAPPLICABLE_CANDIDATE!>plus<!> 1
x < 1 x <!INAPPLICABLE_CANDIDATE!><<!> 1
<!UNRESOLVED_REFERENCE!>x += 1<!> <!UNRESOLVED_REFERENCE!>x += 1<!>
x == 1 x == 1
@@ -29,4 +29,4 @@ fun f(): Unit {
false || y false || y
y && true y && true
y && 1 y && 1
} }
@@ -1,6 +1,6 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
fun test() { fun test() {
if (<!UNRESOLVED_REFERENCE!>x<!> > 0) { if (<!UNRESOLVED_REFERENCE!>x<!> <!UNRESOLVED_REFERENCE!>><!> 0) {
} }
} }
@@ -13,5 +13,5 @@ public class B {
// FILE: main.kt // FILE: main.kt
fun test() { fun test() {
B.bar() { it.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() > 0 } B.bar() { it.<!INAPPLICABLE_CANDIDATE!>hashCode<!>() <!UNRESOLVED_REFERENCE!>><!> 0 }
} }
@@ -1,2 +1,2 @@
// !WITH_NEW_INFERENCE // !WITH_NEW_INFERENCE
val unwrapped = <!UNRESOLVED_REFERENCE!>some<!><<!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!>sdf<!>()()<!><out Any><!>::unwrap<!> val unwrapped = <!UNRESOLVED_REFERENCE!>some<!><!UNRESOLVED_REFERENCE!><<!><!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!><!UNRESOLVED_REFERENCE!>sdf<!>()()<!><out Any><!>::unwrap<!>
@@ -8,9 +8,9 @@ fun composite() {
} }
fun html() { fun html() {
<!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>html<!>><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!UNRESOLVED_REFERENCE!>html<!>><!SYNTAX!><!> <!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>html<!><!UNRESOLVED_REFERENCE!>><!><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!UNRESOLVED_REFERENCE!>html<!>><!SYNTAX!><!>
} }
fun html1() { fun html1() {
<!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>html<!>><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!UNRESOLVED_REFERENCE!>html<!>><!UNRESOLVED_REFERENCE!>html<!> <!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>html<!><!UNRESOLVED_REFERENCE!>><!><!SYNTAX!><<!><!UNRESOLVED_REFERENCE!>/<!><!UNRESOLVED_REFERENCE!>html<!>><!UNRESOLVED_REFERENCE!>html<!>
} }
@@ -11,7 +11,7 @@ fun takeFirst(expr: StringBuilder): Char {
fun evaluateArg(expr: CharSequence, numbers: ArrayList<Int>): Int { fun evaluateArg(expr: CharSequence, numbers: ArrayList<Int>): Int {
if (expr.length == 0) throw Exception("Syntax error: Character expected"); if (expr.length == 0) throw Exception("Syntax error: Character expected");
val c = <!INAPPLICABLE_CANDIDATE!>takeFirst<!>(expr) val c = <!INAPPLICABLE_CANDIDATE!>takeFirst<!>(expr)
if (c >= '0' && c <= '9') { if (c <!UNRESOLVED_REFERENCE!>>=<!> '0' && c <!UNRESOLVED_REFERENCE!><=<!> '9') {
val n = c <!UNRESOLVED_REFERENCE!>-<!> '0' val n = c <!UNRESOLVED_REFERENCE!>-<!> '0'
if (!numbers.contains(n)) throw Exception("You used incorrect number: " + n) if (!numbers.contains(n)) throw Exception("You used incorrect number: " + n)
numbers.remove(n) numbers.remove(n)
@@ -3,6 +3,6 @@
// See EA-76890 / KT-10843: NPE during analysis // See EA-76890 / KT-10843: NPE during analysis
fun lambda(x : Int?) = x?.<!UNRESOLVED_REFERENCE!>let<!> <!UNRESOLVED_REFERENCE!>l<!> { fun lambda(x : Int?) = x?.<!UNRESOLVED_REFERENCE!>let<!> <!UNRESOLVED_REFERENCE!>l<!> {
y -> y ->
if (y > 0) return@l x if (y <!UNRESOLVED_REFERENCE!>><!> 0) return@l x
y y
}!! }!!
@@ -5,7 +5,7 @@ class A(val x: String?) {
fun foo(other: A) { fun foo(other: A) {
when { when {
x == null && other.x == null -> "1" x == null && other.x == null -> "1"
x.<!INAPPLICABLE_CANDIDATE!>length<!> > 0 -> "2" x.<!INAPPLICABLE_CANDIDATE!>length<!> <!UNRESOLVED_REFERENCE!>><!> 0 -> "2"
} }
} }
} }
@@ -3,7 +3,7 @@
fun indexOfMax(a: IntArray): Int? { fun indexOfMax(a: IntArray): Int? {
var maxI: Int? = 0 var maxI: Int? = 0
a.forEachIndexed { i, value -> a.forEachIndexed { i, value ->
if (value >= <!INAPPLICABLE_CANDIDATE!>a[maxI]<!>) { if (value <!AMBIGUITY!>>=<!> <!INAPPLICABLE_CANDIDATE!>a[maxI]<!>) {
maxI = i maxI = i
} }
else if (value < 0) { else if (value < 0) {
@@ -11,4 +11,4 @@ fun indexOfMax(a: IntArray): Int? {
} }
} }
return maxI return maxI
} }
@@ -396,6 +396,12 @@ class FirVisualizer(private val firFile: FirFile) : BaseRenderer() {
data.append("operator call ${operatorCall.operation}") data.append("operator call ${operatorCall.operation}")
} }
override fun visitComparisonExpression(comparisonExpression: FirComparisonExpression, data: StringBuilder) {
data.append("CMP(${comparisonOperator.operation.operator}, ")
comparisonOperator.compareToCall.accept(this, data)
data.append(")")
}
override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: StringBuilder) { override fun visitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall, data: StringBuilder) {
//skip rendering for as/as?/is/!is //skip rendering for as/as?/is/!is
} }