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;
}
}