Lambda expressions (no closures yet).

This commit is contained in:
Dmitry Petrov
2016-08-26 11:29:51 +03:00
committed by Dmitry Petrov
parent 0b647ac358
commit 83c3bdd788
17 changed files with 285 additions and 36 deletions
+5
View File
@@ -236,6 +236,11 @@
<scope name="idea openapi" level="WARNING" enabled="true" />
<scope name="runtime.classes" level="ERROR" enabled="true" />
</inspection_tool>
<inspection_tool class="LoggerInitializedWithForeignClass" enabled="false" level="WARNING" enabled_by_default="false">
<option name="loggerClassName" value="org.apache.log4j.Logger,org.slf4j.LoggerFactory,org.apache.commons.logging.LogFactory,java.util.logging.Logger" />
<option name="loggerFactoryMethodName" value="getLogger,getLogger,getLog,getLogger" />
</inspection_tool>
<inspection_tool class="LoopToCallChain" enabled="false" level="INFO" enabled_by_default="false" />
<inspection_tool class="MethodMayBeStatic" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_onlyPrivateOrFinal" value="false" />
<option name="m_ignoreEmptyMethods" value="true" />
@@ -16,10 +16,12 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtFunctionLiteral
import org.jetbrains.kotlin.psi.KtLoopExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
@@ -30,8 +32,6 @@ class BodyGenerator(val scopeOwner: CallableDescriptor, override val context: Ge
private val loopTable = HashMap<KtLoopExpression, IrLoop>()
fun generateFunctionBody(ktBody: KtExpression): IrBody {
resetInternalContext()
val statementGenerator = createStatementGenerator()
val irBlockBody = IrBlockBodyImpl(ktBody.startOffset, ktBody.endOffset)
@@ -42,21 +42,17 @@ class BodyGenerator(val scopeOwner: CallableDescriptor, override val context: Ge
}
else {
val irBodyExpression = statementGenerator.generateExpression(ktBody)
val irReturn = IrReturnImpl(ktBody.startOffset, ktBody.endOffset, context.builtIns.nothingType, scopeOwner, irBodyExpression)
irBlockBody.addStatement(irReturn)
irBlockBody.addStatement(irBodyExpression.wrapWithReturn())
}
postprocessFunctionBody()
return irBlockBody
}
private fun resetInternalContext() {
loopTable.clear()
}
private fun postprocessFunctionBody() {
}
private fun IrExpression.wrapWithReturn() =
if (KotlinBuiltIns.isNothing(type))
this
else
IrReturnImpl(startOffset, endOffset, context.builtIns.nothingType, scopeOwner, this)
fun generatePropertyInitializerBody(ktInitializer: KtExpression): IrBody =
IrExpressionBodyImpl(ktInitializer.startOffset, ktInitializer.endOffset,
@@ -71,5 +67,24 @@ class BodyGenerator(val scopeOwner: CallableDescriptor, override val context: Ge
fun getLoop(expression: KtExpression): IrLoop? =
loopTable[expression]
fun generateLambdaBody(ktFun: KtFunctionLiteral): IrBody {
val statementGenerator = createStatementGenerator()
val ktBody = ktFun.bodyExpression!!
val irBlockBody = IrBlockBodyImpl(ktBody.startOffset, ktBody.endOffset)
if (ktBody is KtBlockExpression) {
for (ktStatement in ktBody.statements.subList(0, ktBody.statements.size - 1)) {
irBlockBody.addStatement(statementGenerator.generateStatement(ktStatement))
}
val ktReturnedValue = ktBody.statements.last()
irBlockBody.addStatement(statementGenerator.generateExpression(ktReturnedValue).wrapWithReturn())
}
else {
irBlockBody.addStatement(statementGenerator.generateExpression(ktBody).wrapWithReturn())
}
return irBlockBody
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.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.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext
class LocalFunctionGenerator(val statementGenerator: StatementGenerator) : GeneratorWithScope {
override val scope: Scope get() = statementGenerator.scope
override val context: GeneratorContext get() = statementGenerator.context
fun generateLambda(ktLambda: KtLambdaExpression): IrStatement {
val ktFun = ktLambda.functionLiteral
val lambdaExpressionType = getInferredTypeWithSmartcastsOrFail(ktLambda)
val lambdaDescriptor = getOrFail(BindingContext.FUNCTION, ktFun)
val irBlock = IrBlockImpl(ktLambda.startOffset, ktLambda.endOffset, lambdaExpressionType, IrOperator.LAMBDA)
val irFun = IrFunctionImpl(ktFun.startOffset, ktFun.endOffset, IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA, lambdaDescriptor)
irFun.body = BodyGenerator(lambdaDescriptor, statementGenerator.context).generateLambdaBody(ktFun)
irBlock.addStatement(irFun)
irBlock.addStatement(IrCallableReferenceImpl(ktLambda.startOffset, ktLambda.endOffset, lambdaExpressionType, lambdaDescriptor))
return irBlock
}
}
@@ -26,7 +26,9 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.deparenthesize
import org.jetbrains.kotlin.psi2ir.intermediate.*
import org.jetbrains.kotlin.psi2ir.intermediate.IntermediateValue
import org.jetbrains.kotlin.psi2ir.intermediate.createRematerializableOrTemporary
import org.jetbrains.kotlin.psi2ir.intermediate.setExplicitReceiverValue
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -40,10 +42,11 @@ import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import java.lang.AssertionError
class StatementGenerator(
override val context: GeneratorContext,
val scopeOwner: DeclarationDescriptor,
val scopeOwner: CallableDescriptor,
val bodyGenerator: BodyGenerator,
override val scope: Scope
) : KtVisitor<IrStatement, Nothing?>(), GeneratorWithScope {
@@ -316,4 +319,7 @@ class StatementGenerator(
override fun visitTryExpression(expression: KtTryExpression, data: Nothing?): IrStatement =
TryCatchExpressionGenerator(this).generateTryCatch(expression)
}
override fun visitLambdaExpression(expression: KtLambdaExpression, data: Nothing?): IrStatement =
LocalFunctionGenerator(this).generateLambda(expression)
}
@@ -42,6 +42,7 @@ enum class IrDeclarationKind {
enum class IrDeclarationOrigin {
DEFINED,
LOCAL_FUNCTION_FOR_LAMBDA,
IR_TEMPORARY_VARIABLE,
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
interface IrCallableReference : IrDeclarationReference {
override val descriptor: CallableDescriptor
// TODO closure
}
class IrCallableReferenceImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
descriptor: CallableDescriptor
) : IrDeclarationReferenceBase<CallableDescriptor>(startOffset, endOffset, type, descriptor), IrCallableReference {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitCallableReference(this, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// TODO
}
override fun getChild(slot: Int): IrElement? {
// TODO
return null
}
override fun replaceChild(slot: Int, newChild: IrElement) {
// TODO
}
}
@@ -85,6 +85,9 @@ interface IrOperator {
object FOR_LOOP_HAS_NEXT : IrOperatorImpl("FOR_LOOP_HAS_NEXT")
object FOR_LOOP_NEXT : IrOperatorImpl("FOR_LOOP_NEXT")
object LAMBDA : IrOperatorImpl("LAMBDA")
data class COMPONENT_N private constructor(val index: Int) : IrOperatorImpl("COMPONENT_$index") {
companion object {
private val precreatedComponents = Array(32) { i -> COMPONENT_N(i + 1) }
@@ -97,8 +100,6 @@ interface IrOperator {
}
}
}
fun IrOperator.isAssignmentOperatorWithResult() =
@@ -67,10 +67,10 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
"BLOCK_BODY"
override fun visitExpression(expression: IrExpression, data: Nothing?): String =
"? ${expression.javaClass.simpleName} type=${expression.renderType()}"
"? ${expression.javaClass.simpleName} type=${expression.type.render()}"
override fun <T> visitConst(expression: IrConst<T>, data: Nothing?): String =
"CONST ${expression.kind} type=${expression.renderType()} value='${expression.value}'"
"CONST ${expression.kind} type=${expression.type.render()} value='${expression.value}'"
override fun visitVararg(expression: IrVararg, data: Nothing?): String =
"VARARG type=${expression.type} varargElementType=${expression.varargElementType}"
@@ -79,35 +79,35 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
"SPREAD_ELEMENT"
override fun visitBlock(expression: IrBlock, data: Nothing?): String =
"BLOCK type=${expression.renderType()} operator=${expression.operator}"
"BLOCK type=${expression.type.render()} operator=${expression.operator}"
override fun visitReturn(expression: IrReturn, data: Nothing?): String =
"RETURN type=${expression.renderType()}"
"RETURN type=${expression.type.render()}"
override fun visitGetExtensionReceiver(expression: IrGetExtensionReceiver, data: Nothing?): String =
"\$RECEIVER of: ${expression.descriptor.containingDeclaration.name} type=${expression.renderType()}"
"\$RECEIVER of: ${expression.descriptor.containingDeclaration.name} type=${expression.type.render()}"
override fun visitThisReference(expression: IrThisReference, data: Nothing?): String =
"THIS ${expression.classDescriptor.render()} type=${expression.renderType()}"
"THIS ${expression.classDescriptor.render()} type=${expression.type.render()}"
override fun visitCall(expression: IrCall, data: Nothing?): String =
"CALL .${expression.descriptor.name} " +
"type=${expression.renderType()} operator=${expression.operator}"
"type=${expression.type.render()} operator=${expression.operator}"
override fun visitGetVariable(expression: IrGetVariable, data: Nothing?): String =
"GET_VAR ${expression.descriptor.name} type=${expression.renderType()} operator=${expression.operator}"
"GET_VAR ${expression.descriptor.name} type=${expression.type.render()} operator=${expression.operator}"
override fun visitSetVariable(expression: IrSetVariable, data: Nothing?): String =
"SET_VAR ${expression.descriptor.name} type=${expression.renderType()} operator=${expression.operator}"
"SET_VAR ${expression.descriptor.name} type=${expression.type.render()} operator=${expression.operator}"
override fun visitGetObjectValue(expression: IrGetObjectValue, data: Nothing?): String =
"GET_OBJECT ${expression.descriptor.name} type=${expression.renderType()}"
"GET_OBJECT ${expression.descriptor.name} type=${expression.type.render()}"
override fun visitGetEnumValue(expression: IrGetEnumValue, data: Nothing?): String =
"GET_ENUM_VALUE ${expression.descriptor.name} type=${expression.renderType()}"
"GET_ENUM_VALUE ${expression.descriptor.name} type=${expression.type.render()}"
override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): String =
"STRING_CONCATENATION type=${expression.renderType()}"
"STRING_CONCATENATION type=${expression.type.render()}"
override fun visitTypeOperator(expression: IrTypeOperatorCall, data: Nothing?): String =
"TYPE_OP operator=${expression.operator} typeOperand=${expression.typeOperand.render()}"
@@ -128,16 +128,19 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
"CONTINUE label=${jump.label} depth=${jump.getDepth()}"
override fun visitThrow(expression: IrThrow, data: Nothing?): String =
"THROW type=${expression.renderType()}"
"THROW type=${expression.type.render()}"
override fun visitCallableReference(expression: IrCallableReference, data: Nothing?): String =
"CALLABLE_REFERENCE ${expression.descriptor.render()} type=${expression.type.render()}"
override fun visitTryCatch(tryCatch: IrTryCatch, data: Nothing?): String =
"TRY_CATCH type=${tryCatch.renderType()}"
"TRY_CATCH type=${tryCatch.type.render()}"
override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String =
"DUMMY ${declaration.descriptor.javaClass.simpleName} ${declaration.descriptor.name}"
override fun visitDummyExpression(expression: IrDummyExpression, data: Nothing?): String =
"DUMMY ${expression.description} type=${expression.renderType()}"
"DUMMY ${expression.description} type=${expression.type.render()}"
companion object {
private val DESCRIPTOR_RENDERER = DescriptorRenderer.withOptions {
@@ -155,9 +158,6 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
internal fun DeclarationDescriptor?.render(): String =
this?.let { DESCRIPTOR_RENDERER.render(it) } ?: "<none>"
internal fun IrExpression.renderType(): String =
type.render()
internal fun KotlinType?.render(): String =
this?.let { DESCRIPTOR_RENDERER.renderType(it) } ?: "<no-type>"
}
@@ -58,6 +58,7 @@ interface IrElementVisitor<out R, in D> {
fun visitSetVariable(expression: IrSetVariable, data: D) = visitDeclarationReference(expression, data)
fun visitGetExtensionReceiver(expression: IrGetExtensionReceiver, data: D) = visitDeclarationReference(expression, data)
fun visitCall(expression: IrCall, data: D) = visitDeclarationReference(expression, data)
fun visitCallableReference(expression: IrCallableReference, data: D) = visitDeclarationReference(expression, data)
fun visitTypeOperator(expression: IrTypeOperatorCall, data: D) = visitExpression(expression, data)
@@ -2,7 +2,12 @@ FILE /variableAsFunctionCall.kt
FUN public fun kotlin.String.k(): () -> kotlin.String
BLOCK_BODY
RETURN type=kotlin.Nothing
DUMMY KtLambdaExpression type=() -> kotlin.String
BLOCK type=() -> kotlin.String operator=LAMBDA
FUN local final fun <anonymous>(): kotlin.String
BLOCK_BODY
RETURN type=kotlin.Nothing
$RECEIVER of: k type=kotlin.String
CALLABLE_REFERENCE local final fun <anonymous>(): kotlin.String type=() -> kotlin.String
FUN public fun test1(/*0*/ f: () -> kotlin.Unit): kotlin.Unit
BLOCK_BODY
RETURN type=kotlin.Nothing
@@ -0,0 +1 @@
fun test1() = "42".run { length }
+13
View File
@@ -0,0 +1,13 @@
FILE /extensionLambda.kt
FUN public fun test1(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
CALL .run type=kotlin.Int operator=null
$receiver: CONST String type=kotlin.String value='42'
block: BLOCK type=kotlin.String.() -> kotlin.Int operator=LAMBDA
FUN local final fun kotlin.String.<anonymous>(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
CALL .<get-length> type=kotlin.Int operator=GET_PROPERTY
$this: $RECEIVER of: <anonymous> type=kotlin.String
CALLABLE_REFERENCE local final fun kotlin.String.<anonymous>(): kotlin.Int type=kotlin.String.() -> kotlin.Int
+1
View File
@@ -0,0 +1 @@
val lambda = { 42 }
+9
View File
@@ -0,0 +1,9 @@
FILE /justLambda.kt
PROPERTY public val lambda: () -> kotlin.Int getter=null setter=null
EXPRESSION_BODY
BLOCK type=() -> kotlin.Int operator=LAMBDA
FUN local final fun <anonymous>(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
CONST Int type=kotlin.Int value='42'
CALLABLE_REFERENCE local final fun <anonymous>(): kotlin.Int type=() -> kotlin.Int
@@ -0,0 +1,20 @@
object A
object B
interface IFoo {
val A.foo: B get() = B
}
interface IInvoke {
operator fun B.invoke() = 42
}
fun test(fooImpl: IFoo, invokeImpl: IInvoke) {
with(A) {
with(fooImpl) {
with(invokeImpl) {
foo()
}
}
}
}
@@ -0,0 +1,42 @@
FILE /multipleImplicitReceivers.kt
CLASS OBJECT A
CLASS OBJECT B
CLASS INTERFACE IFoo
PROPERTY public open val A.foo: B getter=<get-foo> setter=null
PROPERTY_GETTER public open fun A.<get-foo>(): B property=foo
BLOCK_BODY
RETURN type=kotlin.Nothing
GET_OBJECT B type=B
CLASS INTERFACE IInvoke
FUN public open operator fun B.invoke(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
CONST Int type=kotlin.Int value='42'
FUN public fun test(/*0*/ fooImpl: IFoo, /*1*/ invokeImpl: IInvoke): kotlin.Unit
BLOCK_BODY
CALL .with type=kotlin.Int operator=null
receiver: GET_OBJECT A type=A
block: BLOCK type=A.() -> kotlin.Int operator=LAMBDA
FUN local final fun A.<anonymous>(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
CALL .with type=kotlin.Int operator=null
receiver: GET_VAR fooImpl type=IFoo operator=null
block: BLOCK type=IFoo.() -> kotlin.Int operator=LAMBDA
FUN local final fun IFoo.<anonymous>(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
CALL .with type=kotlin.Int operator=null
receiver: GET_VAR invokeImpl type=IInvoke operator=null
block: BLOCK type=IInvoke.() -> kotlin.Int operator=LAMBDA
FUN local final fun IInvoke.<anonymous>(): kotlin.Int
BLOCK_BODY
RETURN type=kotlin.Nothing
CALL .invoke type=kotlin.Int operator=INVOKE
$this: $RECEIVER of: <anonymous> type=IInvoke
$receiver: CALL .<get-foo> type=B operator=GET_PROPERTY
$this: $RECEIVER of: <anonymous> type=IFoo
$receiver: $RECEIVER of: <anonymous> type=A
CALLABLE_REFERENCE local final fun IInvoke.<anonymous>(): kotlin.Int type=IInvoke.() -> kotlin.Int
CALLABLE_REFERENCE local final fun IFoo.<anonymous>(): kotlin.Int type=IFoo.() -> kotlin.Int
CALLABLE_REFERENCE local final fun A.<anonymous>(): kotlin.Int type=A.() -> kotlin.Int
@@ -352,4 +352,31 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
}
@TestMetadata("compiler/testData/ir/irText/lambdas")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Lambdas extends AbstractIrTextTestCase {
public void testAllFilesPresentInLambdas() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText/lambdas"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("extensionLambda.kt")
public void testExtensionLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/lambdas/extensionLambda.kt");
doTest(fileName);
}
@TestMetadata("justLambda.kt")
public void testJustLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/lambdas/justLambda.kt");
doTest(fileName);
}
@TestMetadata("multipleImplicitReceivers.kt")
public void testMultipleImplicitReceivers() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/lambdas/multipleImplicitReceivers.kt");
doTest(fileName);
}
}
}