From 17758d8c00794fafa812428dec9d6617885338eb Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 24 Aug 2016 19:08:12 +0300 Subject: [PATCH] 'try' --- .../kotlin/psi2ir/generators/Generator.kt | 12 +- .../psi2ir/generators/StatementGenerator.kt | 3 + .../generators/TryCatchExpressionGenerator.kt | 51 ++++++++ .../transformations/InsertImplicitCasts.kt | 11 ++ .../src/org/jetbrains/kotlin/ir/IrSlots.kt | 4 +- .../kotlin/ir/expressions/IrTryCatch.kt | 117 ++++++++++++++++++ .../jetbrains/kotlin/ir/util/DumpIrTree.kt | 16 ++- .../kotlin/ir/util/RenderIrElement.kt | 3 + .../kotlin/ir/visitors/IrElementVisitor.kt | 1 + compiler/testData/ir/irText/tryCatch.kt | 31 +++++ compiler/testData/ir/irText/tryCatch.txt | 38 ++++++ .../kotlin/ir/IrTextTestCaseGenerated.java | 6 + 12 files changed, 285 insertions(+), 8 deletions(-) create mode 100644 compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TryCatchExpressionGenerator.kt create mode 100644 compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrTryCatch.kt create mode 100644 compiler/testData/ir/irText/tryCatch.kt create mode 100644 compiler/testData/ir/irText/tryCatch.txt diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt index 0124d96eb16..3e18dc4ac8f 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/Generator.kt @@ -19,9 +19,7 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.ir.expressions.IrDummyExpression -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext @@ -77,6 +75,14 @@ fun Generator.getReturnType(key: KtExpression): KotlinType? { return getReturnType(key.statements.last()) } + if (key is KtConstantExpression) { + return getInferredTypeWithSmartcasts(key) + } + + if (key is KtStringTemplateExpression) { + return context.builtIns.stringType + } + throw AssertionError("Unexpected expression: $key") } 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 a690a6147ad..bfec932a68d 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 @@ -294,6 +294,9 @@ class StatementGenerator( override fun visitContinueExpression(expression: KtContinueExpression, data: Nothing?): IrStatement = LoopExpressionGenerator(this).generateContinue(expression) + + override fun visitTryExpression(expression: KtTryExpression, data: Nothing?): IrStatement = + TryCatchExpressionGenerator(this).generateTryCatch(expression) } diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TryCatchExpressionGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TryCatchExpressionGenerator.kt new file mode 100644 index 00000000000..1ee07b5c37b --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/TryCatchExpressionGenerator.kt @@ -0,0 +1,51 @@ +/* + * 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.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrTryCatchImpl +import org.jetbrains.kotlin.psi.KtTryExpression +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.BindingContext + +class TryCatchExpressionGenerator(val statementGenerator: StatementGenerator) : GeneratorWithScope { + override val scope: Scope get() = statementGenerator.scope + override val context: GeneratorContext get() = statementGenerator.context + + fun generateTryCatch(ktTry: KtTryExpression): IrExpression { + val resultType = getInferredTypeWithSmartcasts(ktTry) + val irTryCatch = IrTryCatchImpl(ktTry.startOffset, ktTry.endOffset, resultType) + + irTryCatch.tryResult = statementGenerator.generateExpression(ktTry.tryBlock) + + for (ktCatchClause in ktTry.catchClauses) { + val ktCatchParameter = ktCatchClause.catchParameter!! + val ktCatchBody = ktCatchClause.catchBody!! + + val catchParameterDescriptor = getOrFail(BindingContext.VALUE_PARAMETER, ktCatchParameter) + + val irCatchResult = statementGenerator.generateExpression(ktCatchBody) + + irTryCatch.addCatchClause(catchParameterDescriptor, irCatchResult) + } + + irTryCatch.finallyExpression = ktTry.finallyBlock?.let{ statementGenerator.generateExpression(it.finalExpression) } + + return irTryCatch + } +} \ No newline at end of file diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt index b7b6f2b9cda..dbeee5a5ae7 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/transformations/InsertImplicitCasts.kt @@ -103,6 +103,17 @@ class InsertImplicitCasts(val builtIns: KotlinBuiltIns): IrElementVisitor() + private val catchClauseResults = SmartList() + + override val catchClausesCount: Int get() = catchClauseResults.size + + fun addCatchClause(parameter: VariableDescriptor, result: IrExpression) { + result.assertDetached() + result.setTreeLocation(this, catchClausesCount) + catchClauseParameters.add(parameter) + catchClauseResults.add(result) + } + + override fun getNthCatchParameter(n: Int): VariableDescriptor? = + catchClauseParameters.getOrNull(n) + + override fun getNthCatchResult(n: Int): IrExpression? = + catchClauseResults.getOrNull(n) + + override var finallyExpression: IrExpression? = null + set(value) { + value?.assertDetached() + field?.detach() + field = value + value?.setTreeLocation(this, FINALLY_EXPRESSION_SLOT) + } + + override fun getChild(slot: Int): IrElement? = + when { + slot == TRY_RESULT_SLOT -> + tryResult + slot >= 0 -> + catchClauseResults.getOrNull(slot) + slot == FINALLY_EXPRESSION_SLOT -> + finallyExpression + else -> null + } + + override fun replaceChild(slot: Int, newChild: IrElement) { + when { + slot == TRY_RESULT_SLOT -> + tryResult = newChild.assertCast() + slot >= 0 -> + putCatchClauseElement(catchClauseResults, newChild, slot) + slot == FINALLY_EXPRESSION_SLOT -> + finallyExpression = newChild.assertCast() + } + } + + private inline fun putCatchClauseElement(list: MutableList, newChild: IrElement, slot: Int) { + list[slot].detach() + list[slot] = newChild.assertCast() + newChild.setTreeLocation(this, slot) + } + + override fun accept(visitor: IrElementVisitor, data: D): R { + return visitor.visitTryCatch(this, data) + } + + override fun acceptChildren(visitor: IrElementVisitor, data: D) { + tryResult.accept(visitor, data) + for (index in 0 ..catchClausesCount - 1) { + catchClauseResults[index].accept(visitor, data) + } + finallyExpression?.accept(visitor, data) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt index 415bd2a4f92..faadaff7f5e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DumpIrTree.kt @@ -19,10 +19,7 @@ package org.jetbrains.kotlin.ir.util import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.SourceLocationManager import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.expressions.IrCall -import org.jetbrains.kotlin.ir.expressions.IrDoWhileLoop -import org.jetbrains.kotlin.ir.expressions.IrWhen -import org.jetbrains.kotlin.ir.expressions.IrWhileLoop +import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.utils.Printer @@ -80,6 +77,17 @@ class DumpIrTreeVisitor(out: Appendable): IrElementVisitor { } } + override fun visitTryCatch(tryCatch: IrTryCatch, data: String) { + tryCatch.dumpLabeledElementWith(data) { + tryCatch.tryResult.accept(this, "try") + for (i in 0 .. tryCatch.catchClausesCount - 1) { + val catchClauseParameter = tryCatch.getNthCatchParameter(i)!! + tryCatch.getNthCatchResult(i)!!.accept(this, "catch ${catchClauseParameter.name}") + } + tryCatch.finallyExpression?.accept(this, "finally") + } + } + private inline fun IrElement.dumpLabeledElementWith(label: String, body: () -> Unit) { printer.println(accept(elementRenderer, null).withLabel(label)) indented(body) diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt index 11ccab1a754..a5e0ee49d5f 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt @@ -115,6 +115,9 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitThrow(expression: IrThrow, data: Nothing?): String = "THROW type=${expression.renderType()}" + override fun visitTryCatch(tryCatch: IrTryCatch, data: Nothing?): String = + "TRY_CATCH type=${tryCatch.renderType()}" + override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String = "DUMMY ${declaration.descriptor.name}" diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt index 2906614af8f..5d133456f62 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/visitors/IrElementVisitor.kt @@ -59,6 +59,7 @@ interface IrElementVisitor { fun visitLoop(loop: IrLoop, data: D) = visitExpression(loop, data) fun visitWhileLoop(loop: IrWhileLoop, data: D) = visitLoop(loop, data) fun visitDoWhileLoop(loop: IrDoWhileLoop, data: D) = visitLoop(loop, data) + fun visitTryCatch(tryCatch: IrTryCatch, data: D) = visitExpression(tryCatch, data) fun visitBreakContinue(jump: IrBreakContinue, data: D) = visitExpression(jump, data) fun visitBreak(jump: IrBreak, data: D) = visitBreakContinue(jump, data) diff --git a/compiler/testData/ir/irText/tryCatch.kt b/compiler/testData/ir/irText/tryCatch.kt new file mode 100644 index 00000000000..3f6c3dfc340 --- /dev/null +++ b/compiler/testData/ir/irText/tryCatch.kt @@ -0,0 +1,31 @@ +fun test1() { + try { + println() + } + catch (e: Throwable) { + println() + } + finally { + println() + } +} + +fun test2(): Int { + return try { + println() + 42 + } + catch (e: Throwable) { + println() + 24 + } + finally { + println() + } +} + +fun testImplicitCast(a: Any) { + if (a !is String) return + + val t: String = try { a } catch (e: Throwable) { "" } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/tryCatch.txt b/compiler/testData/ir/irText/tryCatch.txt new file mode 100644 index 00000000000..51f87dafa82 --- /dev/null +++ b/compiler/testData/ir/irText/tryCatch.txt @@ -0,0 +1,38 @@ +IrFile /tryCatch.kt + IrFunction public fun test1(): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false operator=null + TRY_CATCH type=kotlin.Unit + try: BLOCK type= hasResult=false operator=null + CALL .println type=kotlin.Unit operator=null + catch e: BLOCK type= hasResult=false operator=null + CALL .println type=kotlin.Unit operator=null + finally: BLOCK type= hasResult=false operator=null + CALL .println type=kotlin.Unit operator=null + IrFunction public fun test2(): kotlin.Int + IrExpressionBody + BLOCK type= hasResult=false operator=null + RETURN type= + TRY_CATCH type=kotlin.Int + try: BLOCK type=kotlin.Int hasResult=true operator=null + CALL .println type=kotlin.Unit operator=null + CONST Int type=kotlin.Int value='42' + catch e: BLOCK type=kotlin.Int hasResult=true operator=null + CALL .println type=kotlin.Unit operator=null + CONST Int type=kotlin.Int value='24' + finally: BLOCK type= hasResult=false operator=null + CALL .println type=kotlin.Unit operator=null + IrFunction public fun testImplicitCast(/*0*/ a: kotlin.Any): kotlin.Unit + IrExpressionBody + BLOCK type= hasResult=false operator=null + WHEN type=kotlin.Unit operator=IF + if: TYPE_OP operator=NOT_INSTANCEOF typeOperand=kotlin.String + GET_VAR a type=kotlin.Any operator=null + then: RETURN type= + VAR val t: kotlin.String + TRY_CATCH type=kotlin.String + try: TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.String + BLOCK type=kotlin.Any hasResult=true operator=null + GET_VAR a type=kotlin.Any operator=null + catch e: BLOCK type=kotlin.String hasResult=true operator=null + CONST String type=kotlin.String value='' diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 4b1856bd5c1..ba835ae9315 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -263,6 +263,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("tryCatch.kt") + public void testTryCatch() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/tryCatch.kt"); + doTest(fileName); + } + @TestMetadata("typeOperators.kt") public void testTypeOperators() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/typeOperators.kt");