diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilder.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilder.java index 2100cb90198..d7e63e1a982 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilder.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilder.java @@ -111,7 +111,7 @@ public interface JetControlFlowBuilder { @NotNull InstructionWithValue createAnonymousObject(@NotNull JetObjectLiteralExpression expression); @NotNull - InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression); + InstructionWithValue createLambda(@NotNull JetFunction expression); @NotNull InstructionWithValue loadStringTemplate(@NotNull JetStringTemplateExpression expression, @NotNull List inputValues); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilderAdapter.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilderAdapter.java index 855568dcaad..b3d81011e37 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilderAdapter.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowBuilderAdapter.java @@ -55,8 +55,8 @@ public abstract class JetControlFlowBuilderAdapter implements JetControlFlowBuil @NotNull @Override - public InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) { - return getDelegateBuilder().createFunctionLiteral(expression); + public InstructionWithValue createLambda(@NotNull JetFunction expression) { + return getDelegateBuilder().createLambda(expression); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java index f96be622e08..5832c02f8f8 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java @@ -1093,17 +1093,25 @@ public class JetControlFlowProcessor { return parent.getParent() instanceof JetDoWhileExpression; } + private void visitFunction(@NotNull JetFunction function) { + processLocalDeclaration(function); + boolean isAnonymousFunction = function instanceof JetFunctionLiteral || function.getName() == null; + if (isAnonymousFunction || (function.isLocal() && !(function.getParent() instanceof JetBlockExpression))) { + builder.createLambda(function); + } + } + @Override public void visitNamedFunction(@NotNull JetNamedFunction function) { - processLocalDeclaration(function); + visitFunction(function); } @Override public void visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression) { mark(expression); JetFunctionLiteral functionLiteral = expression.getFunctionLiteral(); - processLocalDeclaration(functionLiteral); - builder.createFunctionLiteral(expression); + visitFunction(functionLiteral); + copyValue(functionLiteral, expression); } @Override diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/JetControlFlowInstructionsGenerator.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/JetControlFlowInstructionsGenerator.java index 5247db75b17..49dca299a67 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/JetControlFlowInstructionsGenerator.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/pseudocode/JetControlFlowInstructionsGenerator.java @@ -425,8 +425,8 @@ public class JetControlFlowInstructionsGenerator extends JetControlFlowBuilderAd @NotNull @Override - public InstructionWithValue createFunctionLiteral(@NotNull JetFunctionLiteralExpression expression) { - return read(expression); + public InstructionWithValue createLambda(@NotNull JetFunction expression) { + return read(expression instanceof JetFunctionLiteral ? (JetFunctionLiteralExpression) expression.getParent() : expression); } @NotNull diff --git a/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.instructions b/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.instructions new file mode 100644 index 00000000000..643ef537f3c --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.instructions @@ -0,0 +1,80 @@ +== test == +fun test(b: Boolean): (Int) -> Int { + if (b) { + fun(n: Int) = n + 1 + } + else { + fun(n: Int) = n - 1 + } +} +--------------------- +L0: + 1 + v(b: Boolean) + magic[FAKE_INITIALIZER](b: Boolean) -> + w(b|) + 2 mark({ if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 } }) + mark(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 }) + r(b) -> + jf(L2|) NEXT:[mark({ fun(n: Int) = n - 1 }), mark({ fun(n: Int) = n + 1 })] + 3 mark({ fun(n: Int) = n + 1 }) + jmp?(L3) NEXT:[r(fun(n: Int) = n + 1) -> , d(fun(n: Int) = n + 1)] + d(fun(n: Int) = n + 1) NEXT:[] +L3 [after local declaration]: + r(fun(n: Int) = n + 1) -> PREV:[jmp?(L3)] + 2 jmp(L6) NEXT:[merge(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 }|, ) -> ] +L2 [else branch]: + 3 mark({ fun(n: Int) = n - 1 }) PREV:[jf(L2|)] + jmp?(L7) NEXT:[r(fun(n: Int) = n - 1) -> , d(fun(n: Int) = n - 1)] + d(fun(n: Int) = n - 1) NEXT:[] +L7 [after local declaration]: + r(fun(n: Int) = n - 1) -> PREV:[jmp?(L7)] +L6 ['if' expression result]: + 2 merge(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 }|, ) -> PREV:[jmp(L6), r(fun(n: Int) = n - 1) -> ] +L1: + 1 NEXT:[] +error: + PREV:[] +sink: + PREV:[, , d(fun(n: Int) = n + 1), d(fun(n: Int) = n - 1)] +===================== +== anonymous_0 == +fun(n: Int) = n + 1 +--------------------- +L4: + 4 + v(n: Int) + magic[FAKE_INITIALIZER](n: Int) -> + w(n|) + r(n) -> + r(1) -> + mark(n + 1) + call(n + 1, plus|, ) -> + ret(*|) L5 +L5: + NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== +== anonymous_1 == +fun(n: Int) = n - 1 +--------------------- +L8: + 4 + v(n: Int) + magic[FAKE_INITIALIZER](n: Int) -> + w(n|) + r(n) -> + r(1) -> + mark(n - 1) + call(n - 1, minus|, ) -> + ret(*|) L9 +L9: + NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.kt b/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.kt new file mode 100644 index 00000000000..57e8217e40f --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.kt @@ -0,0 +1,8 @@ +fun test(b: Boolean): (Int) -> Int { + if (b) { + fun(n: Int) = n + 1 + } + else { + fun(n: Int) = n - 1 + } +} \ No newline at end of file diff --git a/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.values b/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.values new file mode 100644 index 00000000000..5a5e746726a --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.values @@ -0,0 +1,35 @@ +== test == +fun test(b: Boolean): (Int) -> Int { + if (b) { + fun(n: Int) = n + 1 + } + else { + fun(n: Int) = n - 1 + } +} +--------------------- + : Boolean NEW: magic[FAKE_INITIALIZER](b: Boolean) -> +b : Boolean NEW: r(b) -> +fun(n: Int) = n + 1 : * NEW: r(fun(n: Int) = n + 1) -> +{ fun(n: Int) = n + 1 } : * COPY +fun(n: Int) = n - 1 : * NEW: r(fun(n: Int) = n - 1) -> +{ fun(n: Int) = n - 1 } : * COPY +if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 } : * NEW: merge(if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 }|, ) -> +{ if (b) { fun(n: Int) = n + 1 } else { fun(n: Int) = n - 1 } } : * COPY +===================== +== anonymous_0 == +fun(n: Int) = n + 1 +--------------------- + : Int NEW: magic[FAKE_INITIALIZER](n: Int) -> +n : Int NEW: r(n) -> +1 : Int NEW: r(1) -> +n + 1 : Int NEW: call(n + 1, plus|, ) -> +===================== +== anonymous_1 == +fun(n: Int) = n - 1 +--------------------- + : Int NEW: magic[FAKE_INITIALIZER](n: Int) -> +n : Int NEW: r(n) -> +1 : Int NEW: r(1) -> +n - 1 : Int NEW: call(n - 1, minus|, ) -> +===================== diff --git a/compiler/testData/cfg/declarations/functions/functionAsExpression.instructions b/compiler/testData/cfg/declarations/functions/functionAsExpression.instructions new file mode 100644 index 00000000000..19c39cd6cdc --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/functionAsExpression.instructions @@ -0,0 +1,63 @@ +== test == +fun test() { + val f = fun(n: Int): Int { return 1 } + val g = fun foo(n: Int) = 2 +} +--------------------- +L0: + 1 + 2 mark({ val f = fun(n: Int): Int { return 1 } val g = fun foo(n: Int) = 2 }) + v(val f = fun(n: Int): Int { return 1 }) + jmp?(L2) NEXT:[r(fun(n: Int): Int { return 1 }) -> , d(fun(n: Int): Int { return 1 })] + d(fun(n: Int): Int { return 1 }) NEXT:[] +L2 [after local declaration]: + r(fun(n: Int): Int { return 1 }) -> PREV:[jmp?(L2)] + w(f|) + v(val g = fun foo(n: Int) = 2) + jmp?(L5) NEXT:[r(fun foo(n: Int) = 2) -> , d(fun foo(n: Int) = 2)] + d(fun foo(n: Int) = 2) NEXT:[] +L5 [after local declaration]: + r(fun foo(n: Int) = 2) -> PREV:[jmp?(L5)] + w(g|) +L1: + 1 NEXT:[] +error: + PREV:[] +sink: + PREV:[, , d(fun(n: Int): Int { return 1 }), d(fun foo(n: Int) = 2)] +===================== +== anonymous_0 == +fun(n: Int): Int { return 1 } +--------------------- +L3: + 3 + v(n: Int) + magic[FAKE_INITIALIZER](n: Int) -> + w(n|) + 4 mark({ return 1 }) + r(1) -> + ret(*|) L4 +L4: + 3 NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== +== foo == +fun foo(n: Int) = 2 +--------------------- +L6: + 3 + v(n: Int) + magic[FAKE_INITIALIZER](n: Int) -> + w(n|) + r(2) -> + ret(*|) L7 +L7: + NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/declarations/functions/functionAsExpression.kt b/compiler/testData/cfg/declarations/functions/functionAsExpression.kt new file mode 100644 index 00000000000..eb92b2eb756 --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/functionAsExpression.kt @@ -0,0 +1,4 @@ +fun test() { + val f = fun(n: Int): Int { return 1 } + val g = fun foo(n: Int) = 2 +} \ No newline at end of file diff --git a/compiler/testData/cfg/declarations/functions/functionAsExpression.values b/compiler/testData/cfg/declarations/functions/functionAsExpression.values new file mode 100644 index 00000000000..e3a5cf484ea --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/functionAsExpression.values @@ -0,0 +1,23 @@ +== test == +fun test() { + val f = fun(n: Int): Int { return 1 } + val g = fun foo(n: Int) = 2 +} +--------------------- +fun(n: Int): Int { return 1 } : {<: (Int) -> Int} NEW: r(fun(n: Int): Int { return 1 }) -> +fun foo(n: Int) = 2 : {<: (Int) -> Int} NEW: r(fun foo(n: Int) = 2) -> +===================== +== anonymous_0 == +fun(n: Int): Int { return 1 } +--------------------- + : Int NEW: magic[FAKE_INITIALIZER](n: Int) -> +1 : Int NEW: r(1) -> +return 1 !: * +{ return 1 } !: * COPY +===================== +== foo == +fun foo(n: Int) = 2 +--------------------- + : Int NEW: magic[FAKE_INITIALIZER](n: Int) -> +2 : Int NEW: r(2) -> +===================== diff --git a/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.instructions b/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.instructions new file mode 100644 index 00000000000..b015fb66eb2 --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.instructions @@ -0,0 +1,78 @@ +== test == +fun test(b: Boolean): (Int) -> Int { + if (b) { + fun foo(n: Int) = n + 1 + } + else { + fun bar(n: Int) = n - 1 + } +} +--------------------- +L0: + 1 + v(b: Boolean) + magic[FAKE_INITIALIZER](b: Boolean) -> + w(b|) + 2 mark({ if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 } }) + mark(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }) + r(b) -> + jf(L2|) NEXT:[mark({ fun bar(n: Int) = n - 1 }), mark({ fun foo(n: Int) = n + 1 })] + 3 mark({ fun foo(n: Int) = n + 1 }) + jmp?(L3) NEXT:[jmp(L6), d(fun foo(n: Int) = n + 1)] + d(fun foo(n: Int) = n + 1) NEXT:[] +L3 [after local declaration]: + 2 jmp(L6) NEXT:[merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!, !) -> ] PREV:[jmp?(L3)] +L2 [else branch]: + 3 mark({ fun bar(n: Int) = n - 1 }) PREV:[jf(L2|)] + jmp?(L7) NEXT:[merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!, !) -> , d(fun bar(n: Int) = n - 1)] + d(fun bar(n: Int) = n - 1) NEXT:[] +L6 ['if' expression result]: +L7 [after local declaration]: + 2 merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!, !) -> PREV:[jmp(L6), jmp?(L7)] +L1: + 1 NEXT:[] +error: + PREV:[] +sink: + PREV:[, , d(fun foo(n: Int) = n + 1), d(fun bar(n: Int) = n - 1)] +===================== +== foo == +fun foo(n: Int) = n + 1 +--------------------- +L4: + 4 + v(n: Int) + magic[FAKE_INITIALIZER](n: Int) -> + w(n|) + r(n) -> + r(1) -> + mark(n + 1) + call(n + 1, plus|, ) -> + ret(*|) L5 +L5: + NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== +== bar == +fun bar(n: Int) = n - 1 +--------------------- +L8: + 4 + v(n: Int) + magic[FAKE_INITIALIZER](n: Int) -> + w(n|) + r(n) -> + r(1) -> + mark(n - 1) + call(n - 1, minus|, ) -> + ret(*|) L9 +L9: + NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.kt b/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.kt new file mode 100644 index 00000000000..0e679a7cd92 --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.kt @@ -0,0 +1,8 @@ +fun test(b: Boolean): (Int) -> Int { + if (b) { + fun foo(n: Int) = n + 1 + } + else { + fun bar(n: Int) = n - 1 + } +} \ No newline at end of file diff --git a/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.values b/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.values new file mode 100644 index 00000000000..8e81cdd15ea --- /dev/null +++ b/compiler/testData/cfg/declarations/functions/namedFunctionInBlock.values @@ -0,0 +1,33 @@ +== test == +fun test(b: Boolean): (Int) -> Int { + if (b) { + fun foo(n: Int) = n + 1 + } + else { + fun bar(n: Int) = n - 1 + } +} +--------------------- + : Boolean NEW: magic[FAKE_INITIALIZER](b: Boolean) -> +b : Boolean NEW: r(b) -> +{ fun foo(n: Int) = n + 1 } !: * +{ fun bar(n: Int) = n - 1 } !: * +if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 } : * NEW: merge(if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 }|!, !) -> +{ if (b) { fun foo(n: Int) = n + 1 } else { fun bar(n: Int) = n - 1 } } : * COPY +===================== +== foo == +fun foo(n: Int) = n + 1 +--------------------- + : Int NEW: magic[FAKE_INITIALIZER](n: Int) -> +n : Int NEW: r(n) -> +1 : Int NEW: r(1) -> +n + 1 : Int NEW: call(n + 1, plus|, ) -> +===================== +== bar == +fun bar(n: Int) = n - 1 +--------------------- + : Int NEW: magic[FAKE_INITIALIZER](n: Int) -> +n : Int NEW: r(n) -> +1 : Int NEW: r(1) -> +n - 1 : Int NEW: call(n - 1, minus|, ) -> +===================== diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/AbstractPseudocodeTest.java b/compiler/tests/org/jetbrains/kotlin/cfg/AbstractPseudocodeTest.java index 20b1fee2ab3..69b21b9d1af 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/AbstractPseudocodeTest.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/AbstractPseudocodeTest.java @@ -102,7 +102,10 @@ public abstract class AbstractPseudocodeTest extends KotlinTestWithEnvironment { String label; assert (correspondingElement instanceof JetNamedDeclaration || correspondingElement instanceof JetPropertyAccessor) : "Unexpected element class is pseudocode: " + correspondingElement.getClass(); - if (correspondingElement instanceof JetFunctionLiteral) { + boolean isAnonymousFunction = + correspondingElement instanceof JetFunctionLiteral + || (correspondingElement instanceof JetNamedFunction && correspondingElement.getName() == null); + if (isAnonymousFunction) { label = "anonymous_" + i++; } else if (correspondingElement instanceof JetNamedDeclaration) { diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java index 6afaac0c804..c7e485e3e45 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java @@ -345,12 +345,30 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg/declarations/functions"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("anonymousFunctionInBlock.kt") + public void testAnonymousFunctionInBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.kt"); + doTest(fileName); + } + @TestMetadata("FailFunction.kt") public void testFailFunction() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/FailFunction.kt"); doTest(fileName); } + @TestMetadata("functionAsExpression.kt") + public void testFunctionAsExpression() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/functionAsExpression.kt"); + doTest(fileName); + } + + @TestMetadata("namedFunctionInBlock.kt") + public void testNamedFunctionInBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/namedFunctionInBlock.kt"); + doTest(fileName); + } + @TestMetadata("typeParameter.kt") public void testTypeParameter() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/typeParameter.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index ab0622bdedd..1cf1da2b3c7 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -347,12 +347,30 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg/declarations/functions"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("anonymousFunctionInBlock.kt") + public void testAnonymousFunctionInBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/anonymousFunctionInBlock.kt"); + doTest(fileName); + } + @TestMetadata("FailFunction.kt") public void testFailFunction() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/FailFunction.kt"); doTest(fileName); } + @TestMetadata("functionAsExpression.kt") + public void testFunctionAsExpression() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/functionAsExpression.kt"); + doTest(fileName); + } + + @TestMetadata("namedFunctionInBlock.kt") + public void testNamedFunctionInBlock() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/namedFunctionInBlock.kt"); + doTest(fileName); + } + @TestMetadata("typeParameter.kt") public void testTypeParameter() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/declarations/functions/typeParameter.kt");