41
41
import org .jetbrains .jet .lang .resolve .DescriptorUtils ;
42
42
import org .jetbrains .jet .lang .resolve .calls .model .ResolvedCall ;
43
43
import org .jetbrains .jet .lang .resolve .calls .tail .TailRecursionKind ;
44
+ import org .jetbrains .jet .lang .resolve .scopes .receivers .ExpressionReceiver ;
45
+ import org .jetbrains .jet .lang .resolve .scopes .receivers .ReceiverValue ;
46
+ import org .jetbrains .jet .lang .resolve .scopes .receivers .ThisReceiver ;
44
47
import org .jetbrains .jet .lang .types .JetType ;
45
48
import org .jetbrains .jet .lang .types .lang .KotlinBuiltIns ;
46
49
import org .jetbrains .jet .lexer .JetTokens ;
55
58
import static org .jetbrains .jet .lang .resolve .BindingContext .CAPTURED_IN_CLOSURE ;
56
59
import static org .jetbrains .jet .lang .types .TypeUtils .NO_EXPECTED_TYPE ;
57
60
import static org .jetbrains .jet .lang .resolve .BindingContext .*;
58
- import static org .jetbrains .jet .lang .resolve .calls .tail .TailRecursionKind .IN_TRY ;
59
- import static org .jetbrains .jet .lang .resolve .calls .tail .TailRecursionKind .MIGHT_BE ;
60
- import static org .jetbrains .jet .lang .resolve .calls .tail .TailRecursionKind .NON_TAIL ;
61
+ import static org .jetbrains .jet .lang .resolve .calls .tail .TailRecursionKind .*;
61
62
import static org .jetbrains .jet .lang .types .TypeUtils .noExpectedType ;
62
63
63
64
public class JetFlowInformationProvider {
@@ -653,7 +654,8 @@ public void execute(@NotNull Instruction instruction) {
653
654
if (resolvedCall == null ) return ;
654
655
655
656
// is this a recursive call?
656
- if (!resolvedCall .getResultingDescriptor ().getOriginal ().equals (subroutineDescriptor )) return ;
657
+ CallableDescriptor functionDescriptor = resolvedCall .getResultingDescriptor ();
658
+ if (!functionDescriptor .getOriginal ().equals (subroutineDescriptor )) return ;
657
659
658
660
JetElement element = callInstruction .getElement ();
659
661
//noinspection unchecked
@@ -676,7 +678,9 @@ public void execute(@NotNull Instruction instruction) {
676
678
new TailRecursionDetector (subroutine , callInstruction )
677
679
);
678
680
679
- TailRecursionKind kind = isTail ? MIGHT_BE : NON_TAIL ;
681
+ boolean sameThisObject = sameThisObject (resolvedCall );
682
+
683
+ TailRecursionKind kind = isTail && sameThisObject ? MIGHT_BE : NON_TAIL ;
680
684
681
685
KindAndCall kindAndCall = calls .get (element );
682
686
calls .put (element ,
@@ -708,6 +712,33 @@ public void execute(@NotNull Instruction instruction) {
708
712
}
709
713
}
710
714
715
+ private boolean sameThisObject (ResolvedCall <?> resolvedCall ) {
716
+ // A tail call is not allowed to change dispatch receiver
717
+ // class C {
718
+ // fun foo(other: C) {
719
+ // other.foo(this) // not a tail call
720
+ // }
721
+ // }
722
+ ReceiverParameterDescriptor thisObject = resolvedCall .getResultingDescriptor ().getExpectedThisObject ();
723
+ ReceiverValue thisObjectValue = resolvedCall .getThisObject ();
724
+ if (thisObject == null || !thisObjectValue .exists ()) return true ;
725
+
726
+ DeclarationDescriptor classDescriptor = null ;
727
+ if (thisObjectValue instanceof ThisReceiver ) {
728
+ // foo() -- implicit receiver
729
+ classDescriptor = ((ThisReceiver ) thisObjectValue ).getDeclarationDescriptor ();
730
+ }
731
+ else if (thisObjectValue instanceof ExpressionReceiver ) {
732
+ JetExpression expression = JetPsiUtil .deparenthesize (((ExpressionReceiver ) thisObjectValue ).getExpression ());
733
+ if (expression instanceof JetThisExpression ) {
734
+ // this.foo() -- explicit receiver
735
+ JetThisExpression thisExpression = (JetThisExpression ) expression ;
736
+ classDescriptor = trace .get (BindingContext .REFERENCE_TARGET , thisExpression .getInstanceReference ());
737
+ }
738
+ }
739
+ return thisObject .getContainingDeclaration () == classDescriptor ;
740
+ }
741
+
711
742
private static TailRecursionKind combineKinds (TailRecursionKind kind , @ Nullable TailRecursionKind existingKind ) {
712
743
TailRecursionKind resultingKind ;
713
744
if (existingKind == null || existingKind == kind ) {
0 commit comments