diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java index 40808f2a796..841bf5e3d07 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/expressions/ClosureExpressionsTypingVisitor.java @@ -28,6 +28,7 @@ import org.jetbrains.jet.lang.descriptors.impl.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil; +import org.jetbrains.jet.lang.resolve.name.LabelName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.*; @@ -99,6 +100,11 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { JetBlockExpression bodyExpression = expression.getFunctionLiteral().getBodyExpression(); if (bodyExpression == null) return null; + Name callerName = getCallerName(expression); + if (callerName != null) { + context.labelResolver.enterLabeledElement(new LabelName(callerName.asString()), expression); + } + JetType expectedType = context.expectedType; boolean functionTypeExpected = expectedType != NO_EXPECTED_TYPE && KotlinBuiltIns.getInstance().isFunctionOrExtensionFunctionType( expectedType); @@ -115,9 +121,49 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor { // all checks were done before return JetTypeInfo.create(resultType, context.dataFlowInfo); } + + if (callerName != null) { + context.labelResolver.exitLabeledElement(expression); + } + return DataFlowUtils.checkType(resultType, expression, context, context.dataFlowInfo); } + @Nullable + private static Name getCallerName(@NotNull JetFunctionLiteralExpression expression) { + JetCallExpression callExpression = getContainingCallExpression(expression); + if (callExpression == null) return null; + + JetExpression calleeExpression = callExpression.getCalleeExpression(); + if (calleeExpression instanceof JetSimpleNameExpression) { + JetSimpleNameExpression nameExpression = (JetSimpleNameExpression) calleeExpression; + return nameExpression.getReferencedNameAsName(); + } + + return null; + } + + @Nullable + private static JetCallExpression getContainingCallExpression(JetFunctionLiteralExpression expression) { + PsiElement parent = expression.getParent(); + if (parent instanceof JetCallExpression) { + // f {} + return (JetCallExpression) parent; + } + + if (parent instanceof JetValueArgument) { + // f ({}) or f(p = {}) + JetValueArgument argument = (JetValueArgument) parent; + PsiElement argList = argument.getParent(); + if (argList == null) return null; + PsiElement call = argList.getParent(); + if (call instanceof JetCallExpression) { + return (JetCallExpression) call; + } + } + return null; + } + @NotNull private static AnonymousFunctionDescriptor createFunctionDescriptor( @NotNull JetFunctionLiteralExpression expression, diff --git a/compiler/testData/codegen/box/closures/localReturnWithAutolabel.kt b/compiler/testData/codegen/box/closures/localReturnWithAutolabel.kt new file mode 100644 index 00000000000..ac4b10aabf4 --- /dev/null +++ b/compiler/testData/codegen/box/closures/localReturnWithAutolabel.kt @@ -0,0 +1,19 @@ +fun box(): String { + val a = 1 + val explicitlyReturned = run1 {(): String -> + if (a > 0) + return@run1 "OK" + else "Fail 1" + } + if (explicitlyReturned != "OK") return explicitlyReturned + + val implicitlyReturned = run1 {(): String -> + if (a < 0) + return@run1 "Fail 2" + else "OK" + } + if (implicitlyReturned != "OK") return implicitlyReturned + return "OK" +} + +fun run1(f: () -> T): T { return f() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/AutoLabels.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/AutoLabels.kt new file mode 100644 index 00000000000..1cb5d2d3816 --- /dev/null +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/AutoLabels.kt @@ -0,0 +1,23 @@ +fun f() { + foo {(): Int -> + return@foo 1 + } + foo({(): Int -> + return@foo 1 + } + ) + foo(a = {(): Int -> + return@foo 1 + }) + + foo {(): Int -> + foo { + (): Int -> + return@foo 1 + } + return@foo 1 + } +} + +fun foo(a: Any) {} + diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 20e237ce7ad..4afcba8d7b8 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -2432,6 +2432,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/functionLiterals/return"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("AutoLabels.kt") + public void testAutoLabels() throws Exception { + doTest("compiler/testData/diagnostics/tests/functionLiterals/return/AutoLabels.kt"); + } + @TestMetadata("ForbiddenNonLocalReturnNoType.kt") public void testForbiddenNonLocalReturnNoType() throws Exception { doTest("compiler/testData/diagnostics/tests/functionLiterals/return/ForbiddenNonLocalReturnNoType.kt"); diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index f32e86f7732..9db6fba5f59 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -1204,6 +1204,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest("compiler/testData/codegen/box/closures/localReturn.kt"); } + @TestMetadata("localReturnWithAutolabel.kt") + public void testLocalReturnWithAutolabel() throws Exception { + doTest("compiler/testData/codegen/box/closures/localReturnWithAutolabel.kt"); + } + @TestMetadata("recursiveClosure.kt") public void testRecursiveClosure() throws Exception { doTest("compiler/testData/codegen/box/closures/recursiveClosure.kt");