Properly detect changing 'this' reference in tail calls to members

This commit is contained in:
Andrey Breslav
2013-12-05 19:00:27 +04:00
parent 97319808b6
commit fe33fbe79b
@@ -41,6 +41,9 @@ import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.resolve.calls.tail.TailRecursionKind;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetTokens;
@@ -55,9 +58,7 @@ import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.CAPTURED_IN_CLOSURE;
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
import static org.jetbrains.jet.lang.resolve.BindingContext.*;
import static org.jetbrains.jet.lang.resolve.calls.tail.TailRecursionKind.IN_TRY;
import static org.jetbrains.jet.lang.resolve.calls.tail.TailRecursionKind.MIGHT_BE;
import static org.jetbrains.jet.lang.resolve.calls.tail.TailRecursionKind.NON_TAIL;
import static org.jetbrains.jet.lang.resolve.calls.tail.TailRecursionKind.*;
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
public class JetFlowInformationProvider {
@@ -653,7 +654,8 @@ public class JetFlowInformationProvider {
if (resolvedCall == null) return;
// is this a recursive call?
if (!resolvedCall.getResultingDescriptor().getOriginal().equals(subroutineDescriptor)) return;
CallableDescriptor functionDescriptor = resolvedCall.getResultingDescriptor();
if (!functionDescriptor.getOriginal().equals(subroutineDescriptor)) return;
JetElement element = callInstruction.getElement();
//noinspection unchecked
@@ -676,7 +678,9 @@ public class JetFlowInformationProvider {
new TailRecursionDetector(subroutine, callInstruction)
);
TailRecursionKind kind = isTail ? MIGHT_BE : NON_TAIL;
boolean sameThisObject = sameThisObject(resolvedCall);
TailRecursionKind kind = isTail && sameThisObject ? MIGHT_BE : NON_TAIL;
KindAndCall kindAndCall = calls.get(element);
calls.put(element,
@@ -708,6 +712,33 @@ public class JetFlowInformationProvider {
}
}
private boolean sameThisObject(ResolvedCall<?> resolvedCall) {
// A tail call is not allowed to change dispatch receiver
// class C {
// fun foo(other: C) {
// other.foo(this) // not a tail call
// }
// }
ReceiverParameterDescriptor thisObject = resolvedCall.getResultingDescriptor().getExpectedThisObject();
ReceiverValue thisObjectValue = resolvedCall.getThisObject();
if (thisObject == null || !thisObjectValue.exists()) return true;
DeclarationDescriptor classDescriptor = null;
if (thisObjectValue instanceof ThisReceiver) {
// foo() -- implicit receiver
classDescriptor = ((ThisReceiver) thisObjectValue).getDeclarationDescriptor();
}
else if (thisObjectValue instanceof ExpressionReceiver) {
JetExpression expression = JetPsiUtil.deparenthesize(((ExpressionReceiver) thisObjectValue).getExpression());
if (expression instanceof JetThisExpression) {
// this.foo() -- explicit receiver
JetThisExpression thisExpression = (JetThisExpression) expression;
classDescriptor = trace.get(BindingContext.REFERENCE_TARGET, thisExpression.getInstanceReference());
}
}
return thisObject.getContainingDeclaration() == classDescriptor;
}
private static TailRecursionKind combineKinds(TailRecursionKind kind, @Nullable TailRecursionKind existingKind) {
TailRecursionKind resultingKind;
if (existingKind == null || existingKind == kind) {