From ac8019559726b3c81b0772b4c61d3fa86b6aac32 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Fri, 26 Aug 2016 12:39:27 +0300 Subject: [PATCH] Anonymous function expressions ('fun(...) { ... }') and local functions (no closures yet). --- .../generators/LocalFunctionGenerator.kt | 26 +++++++++++++++++++ .../psi2ir/generators/StatementGenerator.kt | 7 +++-- .../kotlin/ir/expressions/IrOperator.kt | 2 +- .../ir/irText/lambdas/anonymousFunction.kt | 1 + .../ir/irText/lambdas/anonymousFunction.txt | 8 ++++++ .../ir/irText/lambdas/localFunction.kt | 5 ++++ .../ir/irText/lambdas/localFunction.txt | 15 +++++++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 12 +++++++++ 8 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/ir/irText/lambdas/anonymousFunction.kt create mode 100644 compiler/testData/ir/irText/lambdas/anonymousFunction.txt create mode 100644 compiler/testData/ir/irText/lambdas/localFunction.kt create mode 100644 compiler/testData/ir/irText/lambdas/localFunction.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt index 28913c3e6be..67051e76546 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/LocalFunctionGenerator.kt @@ -18,11 +18,13 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.IrBlockImpl import org.jetbrains.kotlin.ir.expressions.IrCallableReferenceImpl import org.jetbrains.kotlin.ir.expressions.IrOperator import org.jetbrains.kotlin.psi.KtLambdaExpression +import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext @@ -46,4 +48,28 @@ class LocalFunctionGenerator(val statementGenerator: StatementGenerator) : Gener return irBlock } + fun generateFunction(ktFun: KtNamedFunction): IrStatement = + if (ktFun.name != null) { + generateFunctionDeclaration(ktFun) + } + else { + // anonymous function expression + val funExpressionType = getInferredTypeWithSmartcastsOrFail(ktFun) + val irBlock = IrBlockImpl(ktFun.startOffset, ktFun.endOffset, funExpressionType, IrOperator.ANONYMOUS_FUNCTION) + + val irFun = generateFunctionDeclaration(ktFun) + irBlock.addStatement(irFun) + + irBlock.addStatement(IrCallableReferenceImpl(ktFun.startOffset, ktFun.endOffset, funExpressionType, irFun.descriptor)) + + irBlock + } + + private fun generateFunctionDeclaration(ktFun: KtNamedFunction): IrFunction { + val funDescriptor = getOrFail(BindingContext.FUNCTION, ktFun) + val irFun = IrFunctionImpl(ktFun.startOffset, ktFun.endOffset, IrDeclarationOrigin.DEFINED, funDescriptor) + irFun.body = BodyGenerator(funDescriptor, statementGenerator.context).generateFunctionBody(ktFun.bodyExpression!!) + return irFun + } + } \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt index 12e302549cf..90626905efc 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt @@ -127,7 +127,7 @@ class StatementGenerator( private fun getReturnExpressionTarget(expression: KtReturnExpression): CallableDescriptor = if (!ExpressionTypingUtils.isFunctionLiteral(scopeOwner) && !ExpressionTypingUtils.isFunctionExpression(scopeOwner)) { - scopeOwner as? CallableDescriptor ?: throw AssertionError("Return not in a callable: $scopeOwner") + scopeOwner ?: throw AssertionError("Return not in a callable: $scopeOwner") } else { val label = expression.getTargetLabel() @@ -140,7 +140,7 @@ class StatementGenerator( BindingContextUtils.getContainingFunctionSkipFunctionLiterals(scopeOwner, true).first } else { - scopeOwner as? CallableDescriptor ?: throw AssertionError("Return not in a callable: $scopeOwner") + scopeOwner ?: throw AssertionError("Return not in a callable: $scopeOwner") } } @@ -321,5 +321,8 @@ class StatementGenerator( override fun visitLambdaExpression(expression: KtLambdaExpression, data: Nothing?): IrStatement = LocalFunctionGenerator(this).generateLambda(expression) + + override fun visitNamedFunction(function: KtNamedFunction, data: Nothing?): IrStatement = + LocalFunctionGenerator(this).generateFunction(function) } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt index 5b6dae85946..5328e028420 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt @@ -86,7 +86,7 @@ interface IrOperator { object FOR_LOOP_NEXT : IrOperatorImpl("FOR_LOOP_NEXT") object LAMBDA : IrOperatorImpl("LAMBDA") - + object ANONYMOUS_FUNCTION : IrOperatorImpl("ANONYMOUS_FUNCTION") data class COMPONENT_N private constructor(val index: Int) : IrOperatorImpl("COMPONENT_$index") { companion object { diff --git a/compiler/testData/ir/irText/lambdas/anonymousFunction.kt b/compiler/testData/ir/irText/lambdas/anonymousFunction.kt new file mode 100644 index 00000000000..374bb644190 --- /dev/null +++ b/compiler/testData/ir/irText/lambdas/anonymousFunction.kt @@ -0,0 +1 @@ +val anonymous = fun() { println() } \ No newline at end of file diff --git a/compiler/testData/ir/irText/lambdas/anonymousFunction.txt b/compiler/testData/ir/irText/lambdas/anonymousFunction.txt new file mode 100644 index 00000000000..6fec27ddc6f --- /dev/null +++ b/compiler/testData/ir/irText/lambdas/anonymousFunction.txt @@ -0,0 +1,8 @@ +FILE /anonymousFunction.kt + PROPERTY public val anonymous: () -> kotlin.Unit getter=null setter=null + EXPRESSION_BODY + BLOCK type=() -> kotlin.Unit operator=ANONYMOUS_FUNCTION + FUN local final fun (): kotlin.Unit + BLOCK_BODY + CALL .println type=kotlin.Unit operator=null + CALLABLE_REFERENCE local final fun (): kotlin.Unit type=() -> kotlin.Unit diff --git a/compiler/testData/ir/irText/lambdas/localFunction.kt b/compiler/testData/ir/irText/lambdas/localFunction.kt new file mode 100644 index 00000000000..5241061d047 --- /dev/null +++ b/compiler/testData/ir/irText/lambdas/localFunction.kt @@ -0,0 +1,5 @@ +fun outer() { + var x = 0 + fun local() { x++ } + local() +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/lambdas/localFunction.txt b/compiler/testData/ir/irText/lambdas/localFunction.txt new file mode 100644 index 00000000000..dc4ae070a91 --- /dev/null +++ b/compiler/testData/ir/irText/lambdas/localFunction.txt @@ -0,0 +1,15 @@ +FILE /localFunction.kt + FUN public fun outer(): kotlin.Unit + BLOCK_BODY + VAR var x: kotlin.Int + CONST Int type=kotlin.Int value='0' + FUN local final fun local(): kotlin.Unit + BLOCK_BODY + BLOCK type=kotlin.Int operator=POSTFIX_INCR + VAR val tmp0: kotlin.Int + GET_VAR x type=kotlin.Int operator=POSTFIX_INCR + SET_VAR x type=kotlin.Unit operator=POSTFIX_INCR + CALL .inc type=kotlin.Int operator=POSTFIX_INCR + $this: GET_VAR tmp0 type=kotlin.Int operator=null + GET_VAR tmp0 type=kotlin.Int operator=null + CALL .local type=kotlin.Unit operator=null diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 1f7daee9b03..b4f8aacfbfa 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -361,6 +361,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText/lambdas"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("anonymousFunction.kt") + public void testAnonymousFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/lambdas/anonymousFunction.kt"); + doTest(fileName); + } + @TestMetadata("extensionLambda.kt") public void testExtensionLambda() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/lambdas/extensionLambda.kt"); @@ -373,6 +379,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("localFunction.kt") + public void testLocalFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/lambdas/localFunction.kt"); + doTest(fileName); + } + @TestMetadata("multipleImplicitReceivers.kt") public void testMultipleImplicitReceivers() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/lambdas/multipleImplicitReceivers.kt");