Added checking for ONLY_LOCAL_RETURN in actual parameter of inline function

This commit is contained in:
Michael Bogdanov
2014-07-08 10:17:06 +04:00
parent 24e20b2dcf
commit 6ddeb85c00
7 changed files with 142 additions and 16 deletions
@@ -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;
}
}
@@ -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;
}
}
@@ -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}"
@@ -0,0 +1,10 @@
import kotlin.InlineOption.ONLY_LOCAL_RETURN
inline fun testSameCaptured(lambdaWithResultCaptured: () -> Unit) : String {
doWork({<!NON_LOCAL_RETURN_NOT_ALLOWED!>lambdaWithResultCaptured<!>()})
return "OK"
}
inline fun <R> doWork(inlineOptions(ONLY_LOCAL_RETURN) job: ()-> R) : R {
return job()
}
@@ -0,0 +1,44 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
fun <R> fun1(p: () -> R) {
inlineFun {
p()
}
}
fun <R> fun1ValueArgument(p: () -> R) {
inlineFun ({
p()
})
}
fun <R> fun3(p: () -> R) {
inlineFun {
<!RETURN_NOT_ALLOWED!>return<!>;
}
}
fun <R> fun3ValueArgument(p: () -> R) {
inlineFun ({
<!RETURN_NOT_ALLOWED!>return<!>;
})
}
fun <R> fun4(p: () -> R) {
inlineFun @lambda {(): R ->
return@lambda p();
}
}
fun <R> fun4ValueArgument(p: () -> R) {
inlineFun (@lambda {(): R ->
return@lambda p();
})
}
inline fun <R> inlineFun(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) {
p()
}
@@ -0,0 +1,26 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
class Z {
inline fun <R> inlineFun(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) {
p()
}
}
fun <R> fun1(p: () -> R) {
Z() inlineFun {
p()
}
}
fun <R> fun3(p: () -> R) {
Z() inlineFun {
<!RETURN_NOT_ALLOWED!>return<!>;
}
}
fun <R> fun4(p: () -> R) {
Z() inlineFun @lambda {(): R ->
return@lambda p();
}
}
@@ -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");