New diagnostic for non-local return annotation

This commit is contained in:
Michael Bogdanov
2014-06-10 11:34:06 +04:00
parent 8092717da4
commit da01a11137
26 changed files with 689 additions and 91 deletions
@@ -594,6 +594,7 @@ public interface Errors {
//Inline and inlinable parameters
DiagnosticFactory2<JetElement, DeclarationDescriptor, DeclarationDescriptor> INVISIBLE_MEMBER_FROM_INLINE = DiagnosticFactory2.create(ERROR, CALL_ELEMENT);
DiagnosticFactory3<JetElement, JetElement, DeclarationDescriptor, DeclarationDescriptor> NON_LOCAL_RETURN_NOT_ALLOWED = DiagnosticFactory3.create(ERROR, CALL_ELEMENT);
DiagnosticFactory2<JetElement, JetNamedDeclaration, DeclarationDescriptor> NOT_YET_SUPPORTED_IN_INLINE = DiagnosticFactory2.create(ERROR);
DiagnosticFactory1<JetFunction, DeclarationDescriptor> NOTHING_TO_INLINE = DiagnosticFactory1.create(WARNING, NAMED_ELEMENT);
DiagnosticFactory2<JetElement, JetExpression, DeclarationDescriptor> USAGE_IS_NOT_INLINABLE = DiagnosticFactory2.create(ERROR);
@@ -515,6 +515,8 @@ public class DefaultErrorMessages {
MAP.put(USAGE_IS_NOT_INLINABLE, "Illegal usage of inline-parameter ''{0}'' in ''{1}''. Annotate the parameter with [noinline]", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES);
MAP.put(NULLABLE_INLINE_PARAMETER, "Inline-parameter ''{0}'' of ''{1}'' must not be nullable. Annotate the parameter with [noinline] or make not nullable", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES);
MAP.put(RECURSION_IN_INLINE, "Inline-function ''{1}'' can't be recursive", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES);
//Inline non Locals
MAP.put(NON_LOCAL_RETURN_NOT_ALLOWED, "Can''t inline ''{0}'' here: it may contain non-local returns. Annotate parameter declaration ''{0}'' with ''inlineOptions(ONLY_LOCAL_RETURNS)''", ELEMENT_TEXT, SHORT_NAMES_IN_TYPES, SHORT_NAMES_IN_TYPES);
MAP.setImmutable();
@@ -884,4 +884,37 @@ public class JetPsiUtil {
return element instanceof JetSimpleNameExpression &&
((JetSimpleNameExpression) element).getReferencedNameElementType() == JetTokens.LABEL_IDENTIFIER;
}
@Nullable
public static JetExpression getParentCallIfPresent(@NotNull JetExpression expression) {
PsiElement parent = expression.getParent();
while (parent != null) {
if (parent instanceof JetBinaryExpression ||
parent instanceof JetUnaryExpression ||
parent instanceof JetLabeledExpression ||
parent instanceof JetDotQualifiedExpression ||
parent instanceof JetCallExpression ||
parent instanceof JetArrayAccessExpression ||
parent instanceof JetMultiDeclaration) {
if (parent instanceof JetLabeledExpression) {
parent = parent.getParent();
continue;
}
//check that it's in inlineable call would be in resolve call of parent
return (JetExpression) parent;
}
else if (parent instanceof JetParenthesizedExpression || parent instanceof JetBinaryExpressionWithTypeRHS) {
parent = parent.getParent();
}
else if (parent instanceof JetValueArgument || parent instanceof JetValueArgumentList) {
parent = parent.getParent();
}
else {
return null;
}
}
return null;
}
}
@@ -328,17 +328,18 @@ public class BindingContextUtils {
}
@NotNull
public static Pair<FunctionDescriptor, PsiElement> getLambdaContainingFunction(
@NotNull FunctionDescriptor lambdaDescriptor,
@NotNull BindingContext context
public static Pair<FunctionDescriptor, PsiElement> getContainingFunctionSkipFunctionLiterals(
@NotNull BindingContext context,
@Nullable DeclarationDescriptor startDescriptor,
boolean strict
) {
FunctionDescriptor containingFunctionDescriptor = lambdaDescriptor;
PsiElement containingFunction;
do {
FunctionDescriptor containingFunctionDescriptor = DescriptorUtils.getParentOfType(startDescriptor, FunctionDescriptor.class, strict);
PsiElement containingFunction = containingFunctionDescriptor != null ? callableDescriptorToDeclaration(context, containingFunctionDescriptor) : null;
while (containingFunction instanceof JetFunctionLiteral) {
containingFunctionDescriptor = DescriptorUtils.getParentOfType(containingFunctionDescriptor, FunctionDescriptor.class);
containingFunction = containingFunctionDescriptor != null ? callableDescriptorToDeclaration(context,
containingFunctionDescriptor) : null;
} while (containingFunction instanceof JetFunctionLiteral);
}
return new Pair<FunctionDescriptor, PsiElement>(containingFunctionDescriptor, containingFunction);
}
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2014 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.jet.lang.resolve;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
public class InlineDescriptorUtils {
public static boolean checkNonLocalReturnUsage(@NotNull DeclarationDescriptor fromFunction, @NotNull JetExpression startExpression, @NotNull BindingTrace trace) {
PsiElement containingFunction = PsiTreeUtil.getParentOfType(startExpression, JetClassOrObject.class, JetDeclarationWithBody.class);
if (containingFunction == null) {
return false;
}
DeclarationDescriptor containingFunctionDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, containingFunction);
if (containingFunctionDescriptor == null) {
return false;
}
BindingContext bindingContext = trace.getBindingContext();
while (containingFunction instanceof JetFunctionLiteral && fromFunction != containingFunctionDescriptor) {
//JetFunctionLiteralExpression
containingFunction = containingFunction.getParent();
boolean isInlinedLambda = false;
JetExpression call = JetPsiUtil.getParentCallIfPresent((JetFunctionLiteralExpression) containingFunction);
if (call != null) {
//TODO: ask sveta
ResolvedCall<?> resolvedCall = bindingContext.get(BindingContext.RESOLVED_CALL, JetPsiUtil.getCalleeExpressionIfAny(call));
CallableDescriptor resultingDescriptor = resolvedCall == null ? null : resolvedCall.getResultingDescriptor();
if (resultingDescriptor instanceof SimpleFunctionDescriptor) {
isInlinedLambda = ((SimpleFunctionDescriptor) resultingDescriptor).getInlineStrategy().isInline();
}
}
if (!isInlinedLambda) {
return false;
}
containingFunctionDescriptor = getContainingClassOrFunctionDescriptor(containingFunctionDescriptor, true);
containingFunction = containingFunctionDescriptor != null
? BindingContextUtils.descriptorToDeclaration(bindingContext, containingFunctionDescriptor)
: null;
}
return fromFunction == containingFunctionDescriptor;
}
@Nullable
public static DeclarationDescriptor getContainingClassOrFunctionDescriptor(@NotNull DeclarationDescriptor descriptor, boolean strict) {
DeclarationDescriptor currentDescriptor = strict ? descriptor.getContainingDeclaration() : descriptor;
while (currentDescriptor != null) {
if (currentDescriptor instanceof FunctionDescriptor || currentDescriptor instanceof ClassDescriptor) {
return currentDescriptor;
}
currentDescriptor = currentDescriptor.getContainingDeclaration();
}
return null;
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve.calls;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -24,11 +25,9 @@ import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.InlineDescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.jet.lang.resolve.calls.model.DefaultValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.VarargValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
@@ -39,6 +38,7 @@ import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class InlineCallResolverExtension implements CallResolverExtension {
@@ -82,69 +82,65 @@ public class InlineCallResolverExtension implements CallResolverExtension {
checkCallWithReceiver(context, targetDescriptor, resolvedCall.getReceiverArgument(), expression);
if (inlinableParameters.contains(targetDescriptor)) {
if (!couldAccessVariable(expression)) {
if (!isInsideCall(expression)) {
context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(expression, expression, descriptor));
}
}
for (ResolvedValueArgument value : resolvedCall.getValueArguments().values()) {
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> entry : resolvedCall.getValueArguments().entrySet()) {
ResolvedValueArgument value = entry.getValue();
ValueParameterDescriptor valueDescriptor = entry.getKey();
if (!(value instanceof DefaultValueArgument)) {
for (ValueArgument argument : value.getArguments()) {
checkValueParameter(context, targetDescriptor, argument, value instanceof VarargValueArgument);
checkValueParameter(context, targetDescriptor, argument, valueDescriptor);
}
}
}
checkVisibility(targetDescriptor, expression, context);
checkRecursion(targetDescriptor, expression, context);
checkRecursion(context, targetDescriptor, expression);
}
private static boolean couldAccessVariable(JetExpression expression) {
PsiElement parent = expression.getParent();
while (parent != null) {
if (parent instanceof JetValueArgument ||
parent instanceof JetBinaryExpression ||
parent instanceof JetUnaryExpression ||
parent instanceof JetLabeledExpression ||
parent instanceof JetDotQualifiedExpression ||
parent instanceof JetCallExpression ||
parent instanceof JetArrayAccessExpression ||
parent instanceof JetMultiDeclaration) {
if (parent instanceof JetLabeledExpression) {
parent = parent.getParent();
continue;
}
else if (parent instanceof JetBinaryExpression) {
JetToken token = JetPsiUtil.getOperationToken((JetOperationExpression) parent);
if (token == JetTokens.EQ || token == JetTokens.ANDAND || token == JetTokens.OROR) {
//assignment
return false;
}
}
//check that it's in inlineable call would be in resolve call of parent
return true;
}
else if (parent instanceof JetParenthesizedExpression || parent instanceof JetBinaryExpressionWithTypeRHS) {
parent = parent.getParent();
}
else {
private static boolean isInsideCall(JetExpression expression) {
JetElement parent = JetPsiUtil.getParentCallIfPresent(expression);
if (parent instanceof JetBinaryExpression) {
JetToken token = JetPsiUtil.getOperationToken((JetOperationExpression) parent);
if (token == JetTokens.EQ || token == JetTokens.ANDAND || token == JetTokens.OROR) {
//assignment
return false;
}
}
return false;
return parent != null;
}
private void checkValueParameter(BasicCallResolutionContext context, CallableDescriptor targetDescriptor, ValueArgument argument, boolean isVararg) {
JetExpression jetExpression = argument.getArgumentExpression();
if (jetExpression == null) {
private void checkValueParameter(
@NotNull BasicCallResolutionContext context,
@NotNull CallableDescriptor targetDescriptor,
@NotNull ValueArgument targetArgument,
@NotNull ValueParameterDescriptor targetParameterDescriptor
) {
JetExpression argumentExpression = targetArgument.getArgumentExpression();
if (argumentExpression == null) {
return;
}
CallableDescriptor varDescriptor = getDescriptor(context, jetExpression);
CallableDescriptor argumentCallee = getCalleeDescriptor(context, argumentExpression, false);
if (varDescriptor != null && inlinableParameters.contains(varDescriptor)) {
checkFunctionCall(context, targetDescriptor, jetExpression, isVararg);
if (argumentCallee != null && inlinableParameters.contains(argumentCallee)) {
boolean isTargetInlineFunction = targetDescriptor instanceof SimpleFunctionDescriptor &&
((SimpleFunctionDescriptor) targetDescriptor).getInlineStrategy().isInline();
if (!isTargetInlineFunction || !isInlinableParameter(targetParameterDescriptor)) {
context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(argumentExpression, argumentExpression, descriptor));
} else {
if (allowsNonLocalReturns(argumentCallee) && !allowsNonLocalReturns(targetParameterDescriptor)) {
context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(argumentExpression, argumentExpression, argumentCallee, descriptor));
} else {
checkNonLocalReturn(context, argumentCallee, argumentExpression);
}
}
}
}
@@ -160,7 +156,7 @@ public class InlineCallResolverExtension implements CallResolverExtension {
JetExpression receiverExpression = null;
if (receiver instanceof ExpressionReceiver) {
receiverExpression = ((ExpressionReceiver) receiver).getExpression();
varDescriptor = getDescriptor(context, receiverExpression);
varDescriptor = getCalleeDescriptor(context, receiverExpression, true);
}
else if (receiver instanceof ExtensionReceiver) {
ExtensionReceiver extensionReceiver = (ExtensionReceiver) receiver;
@@ -174,35 +170,42 @@ public class InlineCallResolverExtension implements CallResolverExtension {
if (inlinableParameters.contains(varDescriptor)) {
//check that it's invoke or inlinable extension
checkFunctionCall(context, targetDescriptor, receiverExpression, false);
checkLambdaInvokeOrExtensionCall(context, varDescriptor, targetDescriptor, receiverExpression);
}
}
@Nullable
private static CallableDescriptor getDescriptor(
private static CallableDescriptor getCalleeDescriptor(
@NotNull BasicCallResolutionContext context,
@NotNull JetExpression expression
@NotNull JetExpression expression,
boolean unwrapVariableAsFunction
) {
// todo ask sveta
ResolvedCall<?> thisCall = context.trace.get(BindingContext.RESOLVED_CALL, expression);
if (unwrapVariableAsFunction && thisCall instanceof VariableAsFunctionResolvedCall) {
return ((VariableAsFunctionResolvedCall) thisCall).getVariableCall().getResultingDescriptor();
}
return thisCall != null ? thisCall.getResultingDescriptor() : null;
}
private void checkFunctionCall(
BasicCallResolutionContext context,
CallableDescriptor targetDescriptor,
JetExpression receiverExpresssion,
boolean isVararg
private void checkLambdaInvokeOrExtensionCall(
@NotNull BasicCallResolutionContext context,
@NotNull CallableDescriptor lambdaDescriptor,
@NotNull CallableDescriptor callDescriptor,
@NotNull JetExpression receiverExpresssion
) {
boolean inlinableCall = isInvokeOrInlineExtension(targetDescriptor);
if (!inlinableCall || isVararg) {
boolean inlinableCall = isInvokeOrInlineExtension(callDescriptor);
if (!inlinableCall) {
context.trace.report(Errors.USAGE_IS_NOT_INLINABLE.on(receiverExpresssion, receiverExpresssion, descriptor));
} else {
checkNonLocalReturn(context, lambdaDescriptor, receiverExpresssion);
}
}
public void checkRecursion(
@NotNull BasicCallResolutionContext context,
@NotNull CallableDescriptor targetDescriptor,
@NotNull JetElement expression,
@NotNull BasicCallResolutionContext context
@NotNull JetElement expression
) {
if (targetDescriptor.getOriginal() == descriptor) {
context.trace.report(Errors.RECURSION_IN_INLINE.on(expression, expression, descriptor));
@@ -248,4 +251,34 @@ public class InlineCallResolverExtension implements CallResolverExtension {
}
return true;
}
private void checkNonLocalReturn(
@NotNull BasicCallResolutionContext context,
@NotNull CallableDescriptor inlinableParameterDescriptor,
@NotNull JetExpression parameterUsage
) {
if (!allowsNonLocalReturns(inlinableParameterDescriptor)) return;
if (!InlineDescriptorUtils.checkNonLocalReturnUsage(descriptor, parameterUsage, context.trace)) {
context.trace.report(Errors.NON_LOCAL_RETURN_NOT_ALLOWED.on(parameterUsage, parameterUsage, inlinableParameterDescriptor, descriptor));
}
}
@Nullable
public static PsiElement getDeclaration(JetExpression expression) {
do {
expression = PsiTreeUtil.getParentOfType(expression, JetDeclaration.class);
} while (expression instanceof JetMultiDeclaration || expression instanceof JetProperty);
return expression;
}
private static boolean allowsNonLocalReturns(CallableDescriptor lambdaDescriptor) {
if (lambdaDescriptor instanceof ValueParameterDescriptor) {
if (InlineUtil.hasOnlyLocalReturn((ValueParameterDescriptor) lambdaDescriptor)) {
//annotated
return false;
}
}
return true;
}
}
@@ -22,16 +22,10 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
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.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.DescriptorResolver;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.*;
import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo;
import org.jetbrains.jet.lang.resolve.calls.model.MutableDataFlowInfoForArguments;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
@@ -451,27 +445,24 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
// In a default value for parameter
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
}
assert parentDeclaration != null;
DeclarationDescriptor declarationDescriptor = context.trace.get(DECLARATION_TO_DESCRIPTOR, parentDeclaration);
FunctionDescriptor containingFunctionDescriptor = DescriptorUtils.getParentOfType(declarationDescriptor, FunctionDescriptor.class, false);
if (expression.getTargetLabel() == null) {
if (containingFunctionDescriptor != null) {
PsiElement containingFunction = BindingContextUtils.callableDescriptorToDeclaration(context.trace.getBindingContext(), containingFunctionDescriptor);
assert containingFunction != null;
if (containingFunction instanceof JetFunctionLiteral) {
Pair<FunctionDescriptor, PsiElement> result = BindingContextUtils
.getLambdaContainingFunction(containingFunctionDescriptor, context.trace.getBindingContext());
containingFunctionDescriptor = result.getFirst();
containingFunction = result.getSecond();
assert parentDeclaration != null;
DeclarationDescriptor declarationDescriptor = context.trace.get(DECLARATION_TO_DESCRIPTOR, parentDeclaration);
Pair<FunctionDescriptor, PsiElement> containingFunInfo =
BindingContextUtils.getContainingFunctionSkipFunctionLiterals(context.trace.getBindingContext(), declarationDescriptor,
false);
FunctionDescriptor containingFunctionDescriptor = containingFunInfo.getFirst();
if (containingFunctionDescriptor != null) {
if (!InlineDescriptorUtils.checkNonLocalReturnUsage(containingFunctionDescriptor, expression, context.trace) ||
containingFunctionDescriptor instanceof ConstructorDescriptor) {
// Unqualified, in a function literal
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE);
}
if (containingFunctionDescriptor != null) {
expectedType = getFunctionExpectedReturnType(containingFunctionDescriptor, (JetElement) containingFunction);
}
expectedType = getFunctionExpectedReturnType(containingFunctionDescriptor, (JetElement) containingFunInfo.getSecond());
}
else {
// Outside a function
@@ -483,7 +474,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
SimpleFunctionDescriptor functionDescriptor = context.trace.get(FUNCTION, labelTargetElement);
if (functionDescriptor != null) {
expectedType = getFunctionExpectedReturnType(functionDescriptor, labelTargetElement);
if (functionDescriptor != containingFunctionDescriptor) {
if (!InlineDescriptorUtils.checkNonLocalReturnUsage(functionDescriptor, expression, context.trace)) {
// Qualified, non-local
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE);
@@ -1,4 +1,4 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -ONLY_LOCAL_RETURN
fun <T, U> Function1<T, U>.minusAssign(p: Function1<T, U>) {}
@@ -0,0 +1,7 @@
inline fun <R> onlyLocal(p: () -> R) {
inlineAll(<!USAGE_IS_NOT_INLINABLE!>p<!>)
}
<!NOTHING_TO_INLINE!>inline fun <R> inlineAll(noinline p: () -> R)<!> {
p()
}
@@ -0,0 +1,24 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
val s = object {
val z = p()
fun a() {
p()
}
}
}
inline fun <R> inlineFun(p: () -> R) {
val s = object {
val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
fun a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}
}
@@ -0,0 +1,49 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
val s = object {
val z = p();
{
doCall {
p()
}
}
fun a() {
doCall {
p()
}
p()
}
}
}
inline fun <R> inlineFun(p: () -> R) {
val s = object {
val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>();
{
doCall {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}
fun a() {
doCall {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}
}
inline fun <R> doCall(p: () -> R) {
p()
}
@@ -0,0 +1,43 @@
fun inlineCallExplicitError(): String {
inlineFun @lamba {
if (true) {
<!RETURN_NOT_ALLOWED_EXPLICIT_RETURN_TYPE_REQUIRED!>return@lamba 2<!>
}
1
}
return "x"
}
fun inlineCall(): String {
inlineFun @lamba {
(): Int ->
if (true) {
return@lamba 2
}
1
}
return "x"
}
inline fun inlineFun(s: () -> Int) {
s()
}
fun noInlineCall(): String {
noInline @lambda {
(): Int ->
if (true) {
return@lambda 2
}
1
}
return "x"
}
fun noInline(s: () -> Int) {
s()
}
@@ -0,0 +1,9 @@
import kotlin.InlineOption.*
inline fun <R> onlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
inlineAll(p)
}
inline fun <R> inlineAll(p: () -> R) {
p()
}
@@ -0,0 +1,17 @@
import kotlin.InlineOption.*
inline fun <R> inlineFunWithAnnotation(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) {
inlineFun {
p()
}
}
inline fun <R> inlineFun2(p: () -> R) {
inlineFun {
p()
}
}
inline fun <R> inlineFun(p: () -> R) {
p()
}
@@ -0,0 +1,31 @@
fun inlineCall(): String {
inlineFun {
if (true) {
return@inlineCall ""
}
1
}
return "x"
}
inline fun inlineFun(s: ()->Int) {
s()
}
fun noInlineCall(): String {
noInline {
if (true) {
<!RETURN_NOT_ALLOWED!>return@noInlineCall ""<!>
}
1
}
return "x"
}
fun noInline(s: ()->Int) {
s()
}
@@ -0,0 +1,16 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun a() {
val z = p()
}<!>
a()
}
inline fun <R> inlineFun(p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>fun a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}<!>
a()
}
@@ -0,0 +1,43 @@
import Kind.EXT_RETURN
import Kind.GLOBAL_RETURN
enum class Kind {
LOCAL
EXT_RETURN
GLOBAL_RETURN
}
class Internal(val value: String)
class External(val value: String)
class Global(val value: String)
fun test1(intKind: Kind, extKind: Kind): Global {
var externalResult = doCall @ext {
() : External ->
val internalResult = doCall @int {
() : Internal ->
if (intKind == Kind.LOCAL) {
return@test1 Global("internal to global")
} else if (intKind == EXT_RETURN) {
return@ext External("internal to external")
}
return@int Internal("internal to local")
}
if (extKind == GLOBAL_RETURN || extKind == EXT_RETURN) {
return Global("external to global")
}
External(internalResult.value + " to local");
}
return Global(externalResult.value + " to exit")
}
public inline fun <R> doCall(block: ()-> R) : R {
return block()
}
@@ -0,0 +1,14 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
{
p()
}()
}
inline fun <R> inlineFun(p: () -> R) {
{
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}()
}
@@ -0,0 +1,24 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION -UNUSED_PARAMETER -UNUSED_VARIABLE -NOTHING_TO_INLINE
import kotlin.InlineOption.*
inline fun <R> inlineFunOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN)p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A {
val z = p()
fun a() {
p()
}
}<!>
}
inline fun <R> inlineFun(p: () -> R) {
<!NOT_YET_SUPPORTED_IN_INLINE!>class A {
val z = <!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
fun a() {
<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>()
}
}<!>
}
@@ -0,0 +1,24 @@
inline fun <R> doCall(p: () -> R) {
p()
}
inline fun <R> doCallInt(p: () -> R): R {
return p()
}
class A {
var result: Int = doCallInt { <!RETURN_NOT_ALLOWED!>return this<!> };
var field: Int
get() {
doCall { return 1 }
return 2
}
set(v: Int) {
doCall {
result = v / 2
return
}
result = v
}
}
@@ -0,0 +1,9 @@
import kotlin.InlineOption.*
inline fun <R> toOnlyLocal(inlineOptions(ONLY_LOCAL_RETURN) p: () -> R) {
p()
}
inline fun <R> inlineAll(p: () -> R) {
toOnlyLocal(<!NON_LOCAL_RETURN_NOT_ALLOWED!>p<!>)
}
@@ -4464,7 +4464,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
}
@TestMetadata("compiler/testData/diagnostics/tests/inline")
@InnerTestClasses({Inline.BinaryExpressions.class, Inline.NonPublicMember.class, Inline.Regressions.class, Inline.UnaryExpressions.class})
@InnerTestClasses({Inline.BinaryExpressions.class, Inline.NonLocalReturns.class, Inline.NonPublicMember.class, Inline.Regressions.class, Inline.UnaryExpressions.class})
public static class Inline extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInInline() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/inline"), Pattern.compile("^(.+)\\.kt$"), true);
@@ -4500,6 +4500,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/inline/extensionOnFunction.kt");
}
@TestMetadata("fromInlineToNoInline.kt")
public void testFromInlineToNoInline() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/fromInlineToNoInline.kt");
}
@TestMetadata("functions.kt")
public void testFunctions() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/functions.kt");
@@ -4628,6 +4633,74 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
}
@TestMetadata("compiler/testData/diagnostics/tests/inline/nonLocalReturns")
public static class NonLocalReturns extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInNonLocalReturns() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/inline/nonLocalReturns"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("anonymousObjects.kt")
public void testAnonymousObjects() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/anonymousObjects.kt");
}
@TestMetadata("anonymousObjectsNested.kt")
public void testAnonymousObjectsNested() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/anonymousObjectsNested.kt");
}
@TestMetadata("explicitReturnType.kt")
public void testExplicitReturnType() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/explicitReturnType.kt");
}
@TestMetadata("fromOnlyLocal.kt")
public void testFromOnlyLocal() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/fromOnlyLocal.kt");
}
@TestMetadata("inlineLambda.kt")
public void testInlineLambda() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/inlineLambda.kt");
}
@TestMetadata("labeledReturn.kt")
public void testLabeledReturn() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/labeledReturn.kt");
}
@TestMetadata("localFun.kt")
public void testLocalFun() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/localFun.kt");
}
@TestMetadata("nestedNonLocals.kt")
public void testNestedNonLocals() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/nestedNonLocals.kt");
}
@TestMetadata("noInlineLambda.kt")
public void testNoInlineLambda() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/noInlineLambda.kt");
}
@TestMetadata("nonInlinedClass.kt")
public void testNonInlinedClass() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/nonInlinedClass.kt");
}
@TestMetadata("propertyAccessorsAndConstructor.kt")
public void testPropertyAccessorsAndConstructor() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/propertyAccessorsAndConstructor.kt");
}
@TestMetadata("toOnlyLocal.kt")
public void testToOnlyLocal() throws Exception {
doTest("compiler/testData/diagnostics/tests/inline/nonLocalReturns/toOnlyLocal.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/inline/nonPublicMember")
public static class NonPublicMember extends AbstractJetDiagnosticsTest {
public void testAllFilesPresentInNonPublicMember() throws Exception {
@@ -4706,6 +4779,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
TestSuite suite = new TestSuite("Inline");
suite.addTestSuite(Inline.class);
suite.addTestSuite(BinaryExpressions.class);
suite.addTestSuite(NonLocalReturns.class);
suite.addTestSuite(NonPublicMember.class);
suite.addTestSuite(Regressions.class);
suite.addTestSuite(UnaryExpressions.class);
+8
View File
@@ -24,3 +24,11 @@ public enum class InlineStrategy {
AS_FUNCTION
IN_PLACE
}
public annotation class inlineOptions(vararg val value: InlineOption)
public enum class InlineOption {
LOCAL_CONTINUE_AND_BREAK
ONLY_LOCAL_RETURN
}
@@ -0,0 +1,22 @@
/*
* Copyright 2010-2014 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.jet.lang.types.lang;
public enum InlineOption {
LOCAL_CONTINUE_AND_BREAK,
ONLY_LOCAL_RETURN
}
@@ -23,9 +23,12 @@ import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.resolve.constants.ArrayValue;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.constants.EnumValue;
import java.util.List;
public class InlineUtil {
public static boolean hasNoinlineAnnotation(@NotNull CallableDescriptor valueParameterDescriptor) {
@@ -68,6 +71,40 @@ public class InlineUtil {
return null;
}
public static boolean hasOnlyLocalContinueAndBreak(@NotNull ValueParameterDescriptor descriptor) {
return hasInlineOption(descriptor, InlineOption.LOCAL_CONTINUE_AND_BREAK);
}
public static boolean hasOnlyLocalReturn(@NotNull ValueParameterDescriptor descriptor) {
return hasInlineOption(descriptor, InlineOption.ONLY_LOCAL_RETURN);
}
private static boolean hasInlineOption(@NotNull ValueParameterDescriptor descriptor, @NotNull InlineOption option) {
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
ClassDescriptor annotationClass = builtIns.getInlineOptionsClassAnnotation();
AnnotationDescriptor optionsAnnotation = getAnnotation(descriptor.getAnnotations(), annotationClass);
if (optionsAnnotation != null) {
ValueParameterDescriptor parameterDescriptor = annotationClass.getConstructors().iterator().next().getValueParameters().get(0);
CompileTimeConstant<?> argument = optionsAnnotation.getValueArgument(parameterDescriptor);
if (argument == null) {
return false;
} else {
if (argument instanceof ArrayValue) {
List<CompileTimeConstant<?>> values = ((ArrayValue) argument).getValue();
for (CompileTimeConstant<?> value : values) {
if (value instanceof EnumValue) {
if (((EnumValue) value).getValue().getName().asString().equals(option.name())) {
return true;
}
}
}
}
}
}
return false;
}
}
@@ -307,6 +307,11 @@ public class KotlinBuiltIns {
return getBuiltInClassByName("inline");
}
@NotNull
public ClassDescriptor getInlineOptionsClassAnnotation() {
return getBuiltInClassByName("inlineOptions");
}
@NotNull
public ClassDescriptor getTailRecursiveAnnotationClass() {
return getBuiltInClassByName("tailRecursive");