From 6ddeb85c00d9f98173b0e3dba6e916b90d11e419 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Tue, 8 Jul 2014 10:17:06 +0400 Subject: [PATCH] Added checking for ONLY_LOCAL_RETURN in actual parameter of inline function --- .../lang/resolve/InlineDescriptorUtils.java | 45 +++++++++++++++++-- .../calls/InlineCallResolverExtension.java | 15 ++----- .../lambdaTransformation/sameCaptured.1.kt | 3 +- ...mbdaWithGlobalReturnsInsideOnlyLocalOne.kt | 10 +++++ .../nonLocalReturns/onlyLocalReturnLambda.kt | 44 ++++++++++++++++++ .../onlyLocalReturnLambdaBinaryExpr.kt | 26 +++++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 15 +++++++ 7 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/inline/nonLocalReturns/lambdaWithGlobalReturnsInsideOnlyLocalOne.kt create mode 100644 compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambda.kt create mode 100644 compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambdaBinaryExpr.kt diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/InlineDescriptorUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/InlineDescriptorUtils.java index 71a1a53c777..2702c1d805d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/InlineDescriptorUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/InlineDescriptorUtils.java @@ -23,7 +23,11 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage; +import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMapping; +import org.jetbrains.jet.lang.resolve.calls.model.ArgumentMatch; import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; +import org.jetbrains.jet.lang.resolve.calls.util.CallMaker; +import org.jetbrains.jet.lang.types.lang.InlineUtil; public class InlineDescriptorUtils { @@ -43,16 +47,23 @@ public class InlineDescriptorUtils { while (containingFunction instanceof JetFunctionLiteral && fromFunction != containingFunctionDescriptor) { //JetFunctionLiteralExpression containingFunction = containingFunction.getParent(); - boolean isInlinedLambda = false; + boolean allowsNonLocalReturns = false; JetExpression call = JetPsiUtil.getParentCallIfPresent((JetFunctionLiteralExpression) containingFunction); if (call != null) { ResolvedCall resolvedCall = BindingContextUtilPackage.getResolvedCall(call, bindingContext); CallableDescriptor resultingDescriptor = resolvedCall == null ? null : resolvedCall.getResultingDescriptor(); - if (resultingDescriptor instanceof SimpleFunctionDescriptor) { - isInlinedLambda = ((SimpleFunctionDescriptor) resultingDescriptor).getInlineStrategy().isInline(); + if (resultingDescriptor instanceof SimpleFunctionDescriptor && + ((SimpleFunctionDescriptor) resultingDescriptor).getInlineStrategy().isInline()) { + ValueArgument argument = getContainingArgument(containingFunction, call); + if (argument != null) { + ArgumentMapping mapping = resolvedCall.getArgumentMapping(argument); + if (mapping instanceof ArgumentMatch) { + allowsNonLocalReturns = allowsNonLocalReturns(((ArgumentMatch) mapping).getValueParameter()); + } + } } } - if (!isInlinedLambda) { + if (!allowsNonLocalReturns) { return false; } @@ -66,6 +77,22 @@ public class InlineDescriptorUtils { return fromFunction == containingFunctionDescriptor; } + @Nullable + public static ValueArgument getContainingArgument(PsiElement expression, @NotNull JetExpression stopAtCall) { + if (expression instanceof JetValueArgument) { + return (JetValueArgument) expression; + } + + while (expression != null && expression.getParent() != stopAtCall) { + PsiElement parent = expression.getParent(); + if (parent instanceof JetValueArgument) { + return (JetValueArgument) parent; + } + expression = parent; + } + return expression != null ? CallMaker.makeValueArgument((JetExpression) expression) : null; + } + @Nullable public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) { DeclarationDescriptor currentDescriptor = strict ? descriptor.getContainingDeclaration() : descriptor; @@ -78,4 +105,14 @@ public class InlineDescriptorUtils { return null; } + + public static boolean allowsNonLocalReturns(@NotNull CallableDescriptor lambdaDescriptor) { + if (lambdaDescriptor instanceof ValueParameterDescriptor) { + if (InlineUtil.hasOnlyLocalReturn((ValueParameterDescriptor) lambdaDescriptor)) { + //annotated + return false; + } + } + return true; + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/InlineCallResolverExtension.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/InlineCallResolverExtension.java index b2579aff75f..34ba11e94e6 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/InlineCallResolverExtension.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/InlineCallResolverExtension.java @@ -24,7 +24,6 @@ import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.diagnostics.Errors; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.DescriptorUtils; -import org.jetbrains.jet.lang.resolve.InlineDescriptorUtils; import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext; import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument; @@ -44,6 +43,9 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import static org.jetbrains.jet.lang.resolve.InlineDescriptorUtils.allowsNonLocalReturns; +import static org.jetbrains.jet.lang.resolve.InlineDescriptorUtils.checkNonLocalReturnUsage; + public class InlineCallResolverExtension implements CallResolverExtension { private final SimpleFunctionDescriptor descriptor; @@ -263,7 +265,7 @@ public class InlineCallResolverExtension implements CallResolverExtension { ) { if (!allowsNonLocalReturns(inlinableParameterDescriptor)) return; - if (!InlineDescriptorUtils.checkNonLocalReturnUsage(descriptor, parameterUsage, context.trace)) { + if (!checkNonLocalReturnUsage(descriptor, parameterUsage, context.trace)) { context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage, inlinableParameterDescriptor, descriptor)); } } @@ -276,13 +278,4 @@ public class InlineCallResolverExtension implements CallResolverExtension { return expression; } - private static boolean allowsNonLocalReturns(CallableDescriptor lambdaDescriptor) { - if (lambdaDescriptor instanceof ValueParameterDescriptor) { - if (InlineUtil.hasOnlyLocalReturn((ValueParameterDescriptor) lambdaDescriptor)) { - //annotated - return false; - } - } - return true; - } } diff --git a/compiler/testData/codegen/boxInline/lambdaTransformation/sameCaptured.1.kt b/compiler/testData/codegen/boxInline/lambdaTransformation/sameCaptured.1.kt index e5719a9a1b4..f64c8ceaab1 100644 --- a/compiler/testData/codegen/boxInline/lambdaTransformation/sameCaptured.1.kt +++ b/compiler/testData/codegen/boxInline/lambdaTransformation/sameCaptured.1.kt @@ -1,4 +1,5 @@ import test.* +import kotlin.InlineOption.* fun testSameCaptured() : String { var result = 0; @@ -6,7 +7,7 @@ fun testSameCaptured() : String { return if (result == 12) "OK" else "fail ${result}" } -inline fun testSameCaptured(lambdaWithResultCaptured: () -> Unit) : String { +inline fun testSameCaptured(inlineOptions(ONLY_LOCAL_RETURN) lambdaWithResultCaptured: () -> Unit) : String { var result = 1; result = doWork({result+=11; lambdaWithResultCaptured(); result}) return if (result == 12) "OK" else "fail ${result}" diff --git a/compiler/testData/diagnostics/tests/inline/nonLocalReturns/lambdaWithGlobalReturnsInsideOnlyLocalOne.kt b/compiler/testData/diagnostics/tests/inline/nonLocalReturns/lambdaWithGlobalReturnsInsideOnlyLocalOne.kt new file mode 100644 index 00000000000..299bd3444b7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/nonLocalReturns/lambdaWithGlobalReturnsInsideOnlyLocalOne.kt @@ -0,0 +1,10 @@ +import kotlin.InlineOption.ONLY_LOCAL_RETURN + +inline fun testSameCaptured(lambdaWithResultCaptured: () -> Unit) : String { + doWork({lambdaWithResultCaptured()}) + return "OK" +} + +inline fun doWork(inlineOptions(ONLY_LOCAL_RETURN) job: ()-> R) : R { + return job() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambda.kt b/compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambda.kt new file mode 100644 index 00000000000..f9d086fba1c --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambda.kt @@ -0,0 +1,44 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE +import kotlin.InlineOption.* + +fun fun1(p: () -> R) { + inlineFun { + p() + } +} + +fun fun1ValueArgument(p: () -> R) { + inlineFun ({ + p() + }) +} + +fun fun3(p: () -> R) { + inlineFun { + return; + } +} + +fun fun3ValueArgument(p: () -> R) { + inlineFun ({ + return; + }) +} + + +fun fun4(p: () -> R) { + inlineFun @lambda {(): R -> + return@lambda p(); + } +} + +fun fun4ValueArgument(p: () -> R) { + inlineFun (@lambda {(): R -> + return@lambda p(); + }) +} + + +inline fun inlineFun(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) { + p() +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambdaBinaryExpr.kt b/compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambdaBinaryExpr.kt new file mode 100644 index 00000000000..cda4db3b27f --- /dev/null +++ b/compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambdaBinaryExpr.kt @@ -0,0 +1,26 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE +import kotlin.InlineOption.* + +class Z { + inline fun inlineFun(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) { + p() + } +} + +fun fun1(p: () -> R) { + Z() inlineFun { + p() + } +} + +fun fun3(p: () -> R) { + Z() inlineFun { + return; + } +} + +fun fun4(p: () -> R) { + Z() inlineFun @lambda {(): R -> + return@lambda p(); + } +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 8a48f351c06..e5764ae6cb6 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -4679,6 +4679,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/labeledReturn.kt"); } + @TestMetadata("lambdaWithGlobalReturnsInsideOnlyLocalOne.kt") + public void testLambdaWithGlobalReturnsInsideOnlyLocalOne() throws Exception { + doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/lambdaWithGlobalReturnsInsideOnlyLocalOne.kt"); + } + @TestMetadata("localFun.kt") public void testLocalFun() throws Exception { doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/localFun.kt"); @@ -4699,6 +4704,16 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/nonInlinedClass.kt"); } + @TestMetadata("onlyLocalReturnLambda.kt") + public void testOnlyLocalReturnLambda() throws Exception { + doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambda.kt"); + } + + @TestMetadata("onlyLocalReturnLambdaBinaryExpr.kt") + public void testOnlyLocalReturnLambdaBinaryExpr() throws Exception { + doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/onlyLocalReturnLambdaBinaryExpr.kt"); + } + @TestMetadata("propertyAccessorsAndConstructor.kt") public void testPropertyAccessorsAndConstructor() throws Exception { doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/propertyAccessorsAndConstructor.kt");