Tail-call detection support in the front-end

This commit is contained in:
Sergey Mashkov
2013-11-26 18:56:18 +04:00
committed by Andrey Breslav
parent 83b4ad4eac
commit 1c81102941
19 changed files with 684 additions and 6 deletions
@@ -280,6 +280,8 @@ public interface Errors {
DiagnosticFactory0<JetParameter> VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetNamedFunction> NO_TAIL_CALLS_FOUND = DiagnosticFactory0.create(WARNING, NAMED_ELEMENT);
// Named parameters
DiagnosticFactory0<JetParameter> DEFAULT_VALUE_NOT_ALLOWED_IN_OVERRIDE = DiagnosticFactory0.create(ERROR, PARAMETER_DEFAULT_VALUE);
@@ -334,6 +336,9 @@ public interface Errors {
DiagnosticFactory2<JetReferenceExpression, JetExpression, JetType> FUNCTION_EXPECTED = DiagnosticFactory2.create(ERROR);
DiagnosticFactory2<JetExpression, JetExpression, Boolean> FUNCTION_CALL_EXPECTED = DiagnosticFactory2.create(ERROR, CALL_EXPRESSION);
DiagnosticFactory0<JetCallExpression> NON_TAIL_RECURSIVE_CALL = DiagnosticFactory0.create(WARNING, CALL_EXPRESSION);
DiagnosticFactory0<JetCallExpression> TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED = DiagnosticFactory0.create(WARNING, CALL_EXPRESSION);
DiagnosticFactory0<PsiElement> NO_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetExpression> NOT_A_CLASS = DiagnosticFactory0.create(ERROR);
@@ -324,6 +324,7 @@ public class DefaultErrorMessages {
MAP.put(ILLEGAL_SELECTOR, "Expression ''{0}'' cannot be a selector (occur after a dot)", TO_STRING);
MAP.put(NO_TAIL_CALLS_FOUND, "A function is marked as tail-recursive but no tail calls are found");
MAP.put(VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION, "A type annotation is required on a value parameter");
MAP.put(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP, "'break' and 'continue' are only allowed inside a loop");
MAP.put(NOT_A_LOOP_LABEL, "The label ''{0}'' does not denote a loop", TO_STRING);
@@ -414,7 +415,8 @@ public class DefaultErrorMessages {
return hasValueParameters ? "..." : "";
}
});
MAP.put(NON_TAIL_RECURSIVE_CALL, "Recursive call is not a tail call");
MAP.put(TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED, "Tail recursion optimization inside try/catch/finally is not supported");
MAP.put(RESULT_TYPE_MISMATCH, "{0} must return {1} but returns {2}", TO_STRING, RENDER_TYPE, RENDER_TYPE);
MAP.put(UNSAFE_INFIX_CALL,
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2013 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.psi;
import org.jetbrains.annotations.NotNull;
public class BacktraceVisitorStatus<T> {
@NotNull
private final T data;
private final boolean abortTrace;
public BacktraceVisitorStatus(@NotNull T data, boolean abortTrace) {
this.data = data;
this.abortTrace = abortTrace;
}
@NotNull
public T getData() {
return data;
}
public boolean isAbortTrace() {
return abortTrace;
}
}
@@ -42,10 +42,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
public class JetPsiUtil {
private JetPsiUtil() {
@@ -1031,4 +1028,37 @@ public class JetPsiUtil {
expression;
return (JetToken) elementType;
}
@NotNull
public static <T> T visitUpwardToRoot(
@NotNull PsiElement element,
@NotNull JetVisitor<BacktraceVisitorStatus<T>, VisitorData<T>> visitor,
T def
) {
List<PsiElement> track = new ArrayList<PsiElement>();
List<PsiElement> view = Collections.unmodifiableList(track);
@NotNull
BacktraceVisitorStatus<T> lastStatus = new BacktraceVisitorStatus<T>(def, true);
VisitorData<T> data = new VisitorData<T>(view);
do {
track.add(element);
PsiElement parent = element.getParent();
if (parent instanceof JetElement) {
JetElement jet = (JetElement) parent;
data.last = element;
data.data = lastStatus.getData();
BacktraceVisitorStatus<T> status = jet.accept(visitor, data);
if (status == null) {
throw new IllegalStateException("visitor has returned null status");
}
lastStatus = status;
}
element = parent;
} while (element != null && !lastStatus.isAbortTrace());
return lastStatus.getData();
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2013 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.psi;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public final class VisitorData<T> {
public T data;
@NotNull
public final List<PsiElement> visitedPath;
public PsiElement last;
public VisitorData(@NotNull List<PsiElement> visitedPath) {
this.visitedPath = visitedPath;
}
}
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl;
@@ -39,6 +40,7 @@ import org.jetbrains.jet.util.slicedmap.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.jet.util.slicedmap.RewritePolicy.DO_NOTHING;
@@ -87,6 +89,10 @@ public interface BindingContext {
new BasicWritableSlice<JetReferenceExpression, DeclarationDescriptor>(DO_NOTHING);
WritableSlice<JetElement, ResolvedCall<? extends CallableDescriptor>> RESOLVED_CALL =
new BasicWritableSlice<JetElement, ResolvedCall<? extends CallableDescriptor>>(DO_NOTHING);
WritableSlice<JetCallExpression, TailRecursionKind> TAIL_RECURSION_CALL =
new BasicWritableSlice<JetCallExpression, TailRecursionKind>(DO_NOTHING, true);
WritableSlice<DeclarationDescriptor, List<JetCallExpression>> FUNCTION_RECURSIVE_CALL_EXPRESSIONS =
new BasicWritableSlice<DeclarationDescriptor, List<JetCallExpression>>(DO_NOTHING, false);
WritableSlice<JetElement, ConstraintSystemCompleter> CONSTRAINT_SYSTEM_COMPLETER = new BasicWritableSlice<JetElement, ConstraintSystemCompleter>(DO_NOTHING);
WritableSlice<JetElement, Call> CALL = new BasicWritableSlice<JetElement, Call>(DO_NOTHING);
@@ -21,9 +21,11 @@ import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.extension.InlineAnalyzerExtension;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -59,6 +61,9 @@ public class FunctionAnalyzerExtension {
((SimpleFunctionDescriptor) functionDescriptor).isInline()) {
list.add(InlineAnalyzerExtension.INSTANCE);
}
if (KotlinBuiltIns.getInstance().isTailRecursive(functionDescriptor)) {
list.add(TailRecursionsFunctionAnalyzerExtension.INSTANCE);
}
return list;
}
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2013 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.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.calls.TailRecursionKind;
import java.util.Collections;
import java.util.List;
import static com.google.common.base.Objects.firstNonNull;
public class TailRecursionsFunctionAnalyzerExtension implements FunctionAnalyzerExtension.AnalyzerExtension {
public static final TailRecursionsFunctionAnalyzerExtension INSTANCE = new TailRecursionsFunctionAnalyzerExtension();
@Override
public void process(
@NotNull FunctionDescriptor descriptor, @NotNull JetNamedFunction function, @NotNull final BindingTrace trace
) {
List<JetCallExpression> callExpressions = firstNonNull(
trace.get(BindingContext.FUNCTION_RECURSIVE_CALL_EXPRESSIONS, descriptor),
Collections.<JetCallExpression>emptyList());
boolean allNonTailRecursiveCalls = Iterables.all(callExpressions, new Predicate<JetCallExpression>() {
@Override
public boolean apply(JetCallExpression callExpression) {
TailRecursionKind recursionKind = trace.get(BindingContext.TAIL_RECURSION_CALL, callExpression);
return recursionKind == null || !recursionKind.isDoGenerateTailRecursion();
}
});
if (allNonTailRecursiveCalls) {
trace.report(Errors.NO_TAIL_CALLS_FOUND.on(function));
}
}
}
@@ -16,10 +16,19 @@
package org.jetbrains.jet.lang.resolve.calls;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.lang.ref.WeakReference;
import java.util.*;
@@ -77,6 +86,15 @@ public class CallResolverExtensionProvider {
extensions.add(new InlineCallResolverExtension(descriptor));
}
}
if (isAnnotatedAsTailRecursive(declarationDescriptor)) {
extensions.add(RecursiveCallRecorderResolverExtension.INSTANCE);
extensions.add(TailRecursionDetectorExtension.INSTANCE);
}
// add your extensions here
extensions.add(DEFAULT);
}
private static boolean isAnnotatedAsTailRecursive(DeclarationDescriptor descriptor) {
return KotlinBuiltIns.getInstance().isTailRecursive(descriptor);
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2010-2013 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.calls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
import java.util.ArrayList;
import java.util.List;
public class RecursiveCallRecorderResolverExtension extends RecursiveCallResolverExtension {
public static final RecursiveCallRecorderResolverExtension INSTANCE = new RecursiveCallRecorderResolverExtension();
@Override
protected <F extends CallableDescriptor> void runImpl(
@NotNull JetCallExpression callExpression,
@NotNull ResolvedCall<F> resolvedCall,
@NotNull BasicCallResolutionContext context
) {
DeclarationDescriptor declarationDescriptor = context.scope.getContainingDeclaration();
List<JetCallExpression> expressions =
context.trace.get(BindingContext.FUNCTION_RECURSIVE_CALL_EXPRESSIONS, declarationDescriptor);
if (expressions == null) {
expressions = new ArrayList<JetCallExpression>(2);
context.trace.record(BindingContext.FUNCTION_RECURSIVE_CALL_EXPRESSIONS, declarationDescriptor, expressions);
}
expressions.add(callExpression);
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2010-2013 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.calls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
public abstract class RecursiveCallResolverExtension implements CallResolverExtension {
@Override
public <F extends CallableDescriptor> void run(
@NotNull ResolvedCall<F> resolvedCall, @NotNull BasicCallResolutionContext context
) {
if (isRecursion(resolvedCall, context) && context.call.getCallElement() instanceof JetCallExpression) {
runImpl((JetCallExpression) context.call.getCallElement(), resolvedCall, context);
}
}
protected abstract <F extends CallableDescriptor> void runImpl(@NotNull JetCallExpression callExpression,
@NotNull ResolvedCall<F> resolvedCall, @NotNull BasicCallResolutionContext context);
private static <F extends CallableDescriptor> boolean isRecursion(
@NotNull ResolvedCall<F> resolvedCall,
@NotNull BasicCallResolutionContext context
) {
return resolvedCall.getResultingDescriptor().getOriginal().equals(context.scope.getContainingDeclaration());
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2013 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.calls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetPsiUtil;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.context.BasicCallResolutionContext;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
public class TailRecursionDetectorExtension extends RecursiveCallResolverExtension {
public static final TailRecursionDetectorExtension INSTANCE = new TailRecursionDetectorExtension();
@Override
protected <F extends CallableDescriptor> void runImpl(
@NotNull JetCallExpression callExpression,
@NotNull ResolvedCall<F> resolvedCall,
@NotNull BasicCallResolutionContext context
) {
BindingTrace trace = context.trace;
TailRecursionKind recursionKind =
JetPsiUtil.visitUpwardToRoot(callExpression, new TailRecursionDetectorVisitor(), TailRecursionKind.MIGHT_BE);
trace.record(BindingContext.TAIL_RECURSION_CALL,
callExpression,
recursionKind);
switch (recursionKind) {
case NON_TAIL:
trace.report(Errors.NON_TAIL_RECURSIVE_CALL.on(callExpression));
break;
case IN_TRY:
trace.report(Errors.TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED.on(callExpression));
break;
default:
break;
}
}
}
@@ -0,0 +1,205 @@
/*
* Copyright 2010-2013 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.calls;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lexer.JetTokens;
public class TailRecursionDetectorVisitor extends JetVisitor<BacktraceVisitorStatus<TailRecursionKind>, VisitorData<TailRecursionKind>> {
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitNamedFunction(
@NotNull JetNamedFunction function, VisitorData<TailRecursionKind> data
) {
return new BacktraceVisitorStatus<TailRecursionKind>(data.data, true);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitTryExpression(
@NotNull JetTryExpression expression, VisitorData<TailRecursionKind> data
) {
return noTailRecursion(TailRecursionKind.IN_TRY);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitCatchSection(
@NotNull JetCatchClause catchClause, VisitorData<TailRecursionKind> data
) {
return continueTrace(data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitFinallySection(
@NotNull JetFinallySection finallySection, VisitorData<TailRecursionKind> data
) {
return continueTrace(data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitBlockExpression(
@NotNull JetBlockExpression expression, VisitorData<TailRecursionKind> data
) {
if (data.data.isReturn()) return continueTrace(data);
PsiElement child = data.last;
JetElement last = findLastJetElement(expression.getLastChild());
if (last != child) { // if last statement
if (!(last instanceof JetReturnExpression)) {
return noTailRecursion();
}
JetReturnExpression returnExpression = (JetReturnExpression) last;
if (returnExpression.getReturnedExpression() != null) {
return noTailRecursion();
}
if (findLastJetElement(returnExpression.getPrevSibling()) != child) {
return noTailRecursion();
}
}
return continueTrace(data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitWhenExpression(
@NotNull JetWhenExpression expression, VisitorData<TailRecursionKind> data
) {
if (expression.getSubjectExpression() == data.last) {
return noTailRecursion();
}
return continueTrace(data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitWhenEntry(
@NotNull JetWhenEntry jetWhenEntry, VisitorData<TailRecursionKind> data
) {
return continueTrace(data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitWhenConditionExpression(
@NotNull JetWhenConditionWithExpression condition, VisitorData<TailRecursionKind> data
) {
return noTailRecursion();
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitIfExpression(
@NotNull JetIfExpression expression, VisitorData<TailRecursionKind> data
) {
if (expression.getCondition() == data.last) {
return noTailRecursion();
}
return continueTrace(data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitBinaryExpression(
@NotNull JetBinaryExpression expression, VisitorData<TailRecursionKind> data
) {
if (expression.getOperationToken() == JetTokens.ELVIS) {
if (expression.getLeft() == data.last) {
return noTailRecursion();
}
else if (expression.getRight() == data.last) {
return continueTrace(data);
}
else {
return noTailRecursion();
}
}
return super.visitBinaryExpression(expression, data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitQualifiedExpression(
@NotNull JetQualifiedExpression expression, VisitorData<TailRecursionKind> data
) {
if (!(expression.getReceiverExpression() instanceof JetThisExpression)) {
return noTailRecursion();
}
return continueTrace(data);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitReturnExpression(
@NotNull JetReturnExpression expression, VisitorData<TailRecursionKind> data
) {
JetExpression returned = expression.getReturnedExpression();
if (returned == null) {
throw new IllegalStateException("Bad case: how could we reach void return?");
}
return continueTrace(data, TailRecursionKind.IN_RETURN);
}
@Override
public BacktraceVisitorStatus<TailRecursionKind> visitJetElement(
@NotNull JetElement element, VisitorData<TailRecursionKind> state
) {
if (element instanceof JetContainerNode) {
return continueTrace(state);
}
if (state.data == TailRecursionKind.MIGHT_BE) {
return noTailRecursion();
}
return continueTrace(state);
}
private static BacktraceVisitorStatus<TailRecursionKind> noTailRecursion() {
return new BacktraceVisitorStatus<TailRecursionKind>(TailRecursionKind.NON_TAIL, true);
}
private static BacktraceVisitorStatus<TailRecursionKind> noTailRecursion(TailRecursionKind status) {
return new BacktraceVisitorStatus<TailRecursionKind>(status, true);
}
private static BacktraceVisitorStatus<TailRecursionKind> continueTrace(VisitorData<TailRecursionKind> data) {
return continueTrace(data, TailRecursionKind.MIGHT_BE);
}
private static BacktraceVisitorStatus<TailRecursionKind> continueTrace(VisitorData<TailRecursionKind> data, TailRecursionKind desiredStatus) {
TailRecursionKind newStatus = data.data.and(desiredStatus);
return new BacktraceVisitorStatus<TailRecursionKind>(newStatus, !newStatus.isDoGenerateTailRecursion());
}
@Nullable
private static JetElement findLastJetElement(@Nullable PsiElement rightNode) {
PsiElement node = rightNode;
while (node != null) {
if (node instanceof JetElement) {
return (JetElement) node;
}
node = node.getPrevSibling();
}
return null;
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2013 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.calls;
public enum TailRecursionKind {
MIGHT_BE(true),
IN_RETURN(true),
IN_TRY(false),
NON_TAIL(false);
private final boolean doGenerateTailRecursion;
TailRecursionKind(boolean doGenerateTailRecursion) {
this.doGenerateTailRecursion = doGenerateTailRecursion;
}
public boolean isDoGenerateTailRecursion() {
return doGenerateTailRecursion;
}
public boolean isReturn() {
return this == IN_RETURN;
}
public TailRecursionKind and(TailRecursionKind b) {
if (this == b) {
return this;
}
if (!this.isDoGenerateTailRecursion()) {
return this;
}
if (!b.isDoGenerateTailRecursion()) {
return this;
}
if (isOneOf(this, b, IN_RETURN)) {
return IN_RETURN;
}
return this;
}
private static boolean isOneOf(TailRecursionKind a, TailRecursionKind b, TailRecursionKind value) {
return a == value || b == value;
}
}
@@ -0,0 +1,3 @@
<!NO_TAIL_CALLS_FOUND!>tailRecursive fun noTails()<!> {
// nothing here
}
@@ -0,0 +1,12 @@
tailRecursive fun badTails(x : Int) : Int {
if (x > 0) {
return 1 + <!NON_TAIL_RECURSIVE_CALL!>badTails<!>(x - 1)
}
else if (x == 10) {
[suppress("NON_TAIL_RECURSIVE_CALL")]
return 1 + badTails(x - 1)
} else if (x == 50) {
return badTails(x - 1)
}
return 0
}
@@ -0,0 +1,6 @@
fun withoutAnnotation(x : Int) : Int {
if (x > 0) {
return 1 + withoutAnnotation(x - 1)
}
return 0
}
@@ -0,0 +1,9 @@
<!NO_TAIL_CALLS_FOUND!>tailRecursive fun test() : Unit<!> {
try {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>test<!>()
} catch (any : Exception) {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>test<!>()
} finally {
<!TAIL_RECURSION_IN_TRY_IS_NOT_SUPPORTED!>test<!>()
}
}
@@ -33,7 +33,7 @@ import org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve;
@InnerTestClasses({JetDiagnosticsTestGenerated.Tests.class, JetDiagnosticsTestGenerated.Script.class})
public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEagerResolve {
@TestMetadata("compiler/testData/diagnostics/tests")
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.CallableReference.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ClassObjects.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.DelegatedProperty.class, Tests.Deparenthesize.class, Tests.Enum.class, Tests.Evaluate.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.Inline.class, Tests.Inner.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Numbers.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Recovery.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.SmartCasts.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.Suppress.class, Tests.ThisAndSuper.class, Tests.Varargs.class, Tests.When.class})
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.CallableReference.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ClassObjects.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.DelegatedProperty.class, Tests.Deparenthesize.class, Tests.Enum.class, Tests.Evaluate.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.Inline.class, Tests.Inner.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Numbers.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Recovery.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.SmartCasts.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.Suppress.class, Tests.TailRecursions.class, Tests.ThisAndSuper.class, Tests.Varargs.class, Tests.When.class})
public static class Tests extends AbstractDiagnosticsTestWithEagerResolve {
@TestMetadata("Abstract.kt")
public void testAbstract() throws Exception {
@@ -6456,6 +6456,34 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
}
}
@TestMetadata("compiler/testData/diagnostics/tests/tailRecursions")
public static class TailRecursions extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInTailRecursions() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/tailRecursions"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("functionWithNoTails.kt")
public void testFunctionWithNoTails() throws Exception {
doTest("compiler/testData/diagnostics/tests/tailRecursions/functionWithNoTails.kt");
}
@TestMetadata("functionWithNonTailRecursions.kt")
public void testFunctionWithNonTailRecursions() throws Exception {
doTest("compiler/testData/diagnostics/tests/tailRecursions/functionWithNonTailRecursions.kt");
}
@TestMetadata("functionWithoutAnnotation.kt")
public void testFunctionWithoutAnnotation() throws Exception {
doTest("compiler/testData/diagnostics/tests/tailRecursions/functionWithoutAnnotation.kt");
}
@TestMetadata("tailRecursionInFinally.kt")
public void testTailRecursionInFinally() throws Exception {
doTest("compiler/testData/diagnostics/tests/tailRecursions/tailRecursionInFinally.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/thisAndSuper")
public static class ThisAndSuper extends AbstractDiagnosticsTestWithEagerResolve {
public void testAllFilesPresentInThisAndSuper() throws Exception {
@@ -6688,6 +6716,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
suite.addTestSuite(Substitutions.class);
suite.addTestSuite(Subtyping.class);
suite.addTest(Suppress.innerSuite());
suite.addTestSuite(TailRecursions.class);
suite.addTestSuite(ThisAndSuper.class);
suite.addTestSuite(Varargs.class);
suite.addTestSuite(When.class);