Anonymous function expressions ('fun(...) { ... }') and local functions (no closures yet).

This commit is contained in:
Dmitry Petrov
2016-08-26 12:39:27 +03:00
committed by Dmitry Petrov
parent 83c3bdd788
commit ac80195597
8 changed files with 73 additions and 3 deletions
@@ -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
}
}
@@ -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)
}
@@ -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 {
@@ -0,0 +1 @@
val anonymous = fun() { println() }
@@ -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 <no name provided>(): kotlin.Unit
BLOCK_BODY
CALL .println type=kotlin.Unit operator=null
CALLABLE_REFERENCE local final fun <no name provided>(): kotlin.Unit type=() -> kotlin.Unit
+5
View File
@@ -0,0 +1,5 @@
fun outer() {
var x = 0
fun local() { x++ }
local()
}
+15
View File
@@ -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
@@ -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");