Skip to content

Commit b5a8ffa

Browse files
committed
Fix resolution of callable references
When there is unsuccessful (e.g invisible) result of one kind (static/non-static) and there is a successful candidate for another kind, choose the latter one. Note, that we have to postpone commiting trace until we choose one of the results, otherwise errors of unsuccessful results are reported TODO: Maybe it makes sense to report all results when all of them are unsuccessful (NONE_APPLICABLE or something like this) #KT-16278 Fixed
1 parent dd61a5b commit b5a8ffa

File tree

4 files changed

+91
-45
lines changed

4 files changed

+91
-45
lines changed

compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DoubleColonExpressionResolver.kt

Lines changed: 72 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@ import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
5858
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
5959
import org.jetbrains.kotlin.types.typeUtil.makeNullable
6060
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
61+
import org.jetbrains.kotlin.utils.yieldIfNotNull
6162
import java.lang.UnsupportedOperationException
6263
import java.util.*
6364
import javax.inject.Inject
65+
import kotlin.coroutines.experimental.buildSequence
6466

6567
sealed class DoubleColonLHS(val type: KotlinType) {
6668
/**
@@ -626,15 +628,18 @@ class DoubleColonExpressionResolver(
626628
}
627629
}
628630

631+
private class ResolutionResultsAndTraceCommitCallback(
632+
val results: OverloadResolutionResults<CallableDescriptor>,
633+
val commitTrace: () -> Unit
634+
)
635+
629636
private fun tryResolveRHSWithReceiver(
630637
traceTitle: String,
631638
receiver: Receiver?,
632639
reference: KtSimpleNameExpression,
633640
outerContext: ResolutionContext<*>,
634641
resolutionMode: ResolveArgumentsMode
635-
): OverloadResolutionResults<CallableDescriptor>? {
636-
checkReservedYield(reference, outerContext.trace)
637-
642+
): ResolutionResultsAndTraceCommitCallback? {
638643
// we should preserve information about `call` because callable references are analyzed two times,
639644
// otherwise there will be not completed calls in trace
640645
val call = outerContext.trace[BindingContext.CALL, reference] ?: CallMaker.makeCall(reference, receiver, null, reference, emptyList())
@@ -652,12 +657,15 @@ class DoubleColonExpressionResolver(
652657
reference, BasicCallResolutionContext.create(newContext, call, CheckArgumentTypesMode.CHECK_CALLABLE_TYPE)
653658
)
654659

655-
val shouldCommitTrace =
656-
if (resolutionMode == ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS) resolutionResults.isSingleResult
657-
else !resolutionResults.isNothing
658-
if (shouldCommitTrace) temporaryTrace.commit()
659-
660-
return if (resolutionResults.isNothing) null else resolutionResults
660+
return when {
661+
resolutionResults.isNothing -> null
662+
else -> ResolutionResultsAndTraceCommitCallback(resolutionResults) {
663+
checkReservedYield(reference, outerContext.trace)
664+
if (resolutionMode != ResolveArgumentsMode.SHAPE_FUNCTION_ARGUMENTS || resolutionResults.isSingleResult) {
665+
temporaryTrace.commit()
666+
}
667+
}
668+
}
661669
}
662670

663671
private fun resolveCallableReferenceRHS(
@@ -668,52 +676,71 @@ class DoubleColonExpressionResolver(
668676
): OverloadResolutionResults<CallableDescriptor>? {
669677
val reference = expression.callableReference
670678

671-
val lhsType = lhs?.type ?:
672-
return tryResolveRHSWithReceiver("resolve callable reference with empty LHS", null, reference, c, mode)
679+
val lhsType =
680+
lhs?.type ?:
681+
return tryResolveRHSWithReceiver("resolve callable reference with empty LHS", null, reference, c, mode)
682+
?.apply { commitTrace() }?.results
673683

674-
when (lhs) {
675-
is DoubleColonLHS.Type -> {
676-
val classifier = lhsType.constructor.declarationDescriptor
677-
if (classifier !is ClassDescriptor) {
678-
c.trace.report(CALLABLE_REFERENCE_LHS_NOT_A_CLASS.on(expression))
679-
return null
680-
}
684+
val resultSequence = buildSequence {
685+
when (lhs) {
686+
is DoubleColonLHS.Type -> {
687+
val classifier = lhsType.constructor.declarationDescriptor
688+
if (classifier !is ClassDescriptor) {
689+
c.trace.report(CALLABLE_REFERENCE_LHS_NOT_A_CLASS.on(expression))
690+
return@buildSequence
691+
}
681692

682-
val qualifier = c.trace.get(BindingContext.QUALIFIER, expression.receiverExpression!!)
683-
if (qualifier is ClassQualifier) {
684-
val possibleStatic = tryResolveRHSWithReceiver(
685-
"resolve unbound callable reference in static scope", qualifier, reference, c, mode
693+
val qualifier = c.trace.get(BindingContext.QUALIFIER, expression.receiverExpression!!)
694+
if (qualifier is ClassQualifier) {
695+
yieldIfNotNull(
696+
tryResolveRHSWithReceiver(
697+
"resolve unbound callable reference in static scope", qualifier, reference, c, mode
698+
)
699+
)
700+
}
701+
702+
yieldIfNotNull(
703+
tryResolveRHSWithReceiver(
704+
"resolve unbound callable reference with receiver", TransientReceiver(lhsType), reference, c, mode
705+
)
686706
)
687-
if (possibleStatic != null) return possibleStatic
688707
}
708+
is DoubleColonLHS.Expression -> {
709+
val expressionReceiver = ExpressionReceiver.create(expression.receiverExpression!!, lhsType, c.trace.bindingContext)
710+
yieldIfNotNull(
711+
tryResolveRHSWithReceiver(
712+
"resolve bound callable reference", expressionReceiver, reference, c, mode
713+
)
714+
)
689715

690-
val possibleWithReceiver = tryResolveRHSWithReceiver(
691-
"resolve unbound callable reference with receiver", TransientReceiver(lhsType), reference, c, mode
692-
)
693-
if (possibleWithReceiver != null) return possibleWithReceiver
694-
}
695-
is DoubleColonLHS.Expression -> {
696-
val expressionReceiver = ExpressionReceiver.create(expression.receiverExpression!!, lhsType, c.trace.bindingContext)
697-
val result = tryResolveRHSWithReceiver(
698-
"resolve bound callable reference", expressionReceiver, reference, c, mode
699-
)
700-
if (result != null) return result
701-
702-
if (lhs.isObjectQualifier) {
703-
val classifier = lhsType.constructor.declarationDescriptor
704-
val calleeExpression = expression.receiverExpression?.getCalleeExpressionIfAny()
705-
if (calleeExpression is KtSimpleNameExpression && classifier is ClassDescriptor) {
706-
val qualifier = ClassQualifier(calleeExpression, classifier)
707-
val possibleStatic = tryResolveRHSWithReceiver(
708-
"resolve object callable reference in static scope", qualifier, reference, c, mode
709-
)
710-
if (possibleStatic != null) return possibleStatic
716+
if (lhs.isObjectQualifier) {
717+
val classifier = lhsType.constructor.declarationDescriptor
718+
val calleeExpression = expression.receiverExpression?.getCalleeExpressionIfAny()
719+
if (calleeExpression is KtSimpleNameExpression && classifier is ClassDescriptor) {
720+
val qualifier = ClassQualifier(calleeExpression, classifier)
721+
yieldIfNotNull(
722+
tryResolveRHSWithReceiver(
723+
"resolve object callable reference in static scope", qualifier, reference, c, mode
724+
)
725+
)
726+
}
711727
}
712728
}
713729
}
714730
}
715731

716-
return null
732+
// TODO: Maybe it makes sense to report all results when all of them are unsuccessful (NONE_APPLICABLE or something like this)
733+
var resultToCommit: ResolutionResultsAndTraceCommitCallback? = null
734+
for (result in resultSequence) {
735+
resultToCommit = result
736+
if (result.results.isSuccess) {
737+
break
738+
}
739+
}
740+
return resultToCommit?.let {
741+
it.commitTrace()
742+
it.results
743+
}
717744
}
718745

719746
companion object {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// SKIP_TXT
2+
// FILE: A.java
3+
class A {
4+
private static void foo() {}
5+
public String foo(int x) {}
6+
}
7+
// FILE: main.kt
8+
fun main() {
9+
(A::foo)(A(), 1)
10+
}

compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,12 @@ public void testNoAmbiguityMemberVsTopLevel() throws Exception {
21992199
doTest(fileName);
22002200
}
22012201

2202+
@TestMetadata("privateStaticAndPublicMember.kt")
2203+
public void testPrivateStaticAndPublicMember() throws Exception {
2204+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/function/privateStaticAndPublicMember.kt");
2205+
doTest(fileName);
2206+
}
2207+
22022208
@TestMetadata("renameOnImport.kt")
22032209
public void testRenameOnImport() throws Exception {
22042210
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/callableReference/function/renameOnImport.kt");

core/util.runtime/src/org/jetbrains/kotlin/utils/collections.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.jetbrains.kotlin.utils
1818

1919
import java.util.*
20+
import kotlin.coroutines.experimental.SequenceBuilder
2021

2122
fun <K, V> Iterable<K>.keysToMap(value: (K) -> V): Map<K, V> {
2223
return associateBy({ it }, value)
@@ -63,6 +64,8 @@ fun <T: Any> MutableCollection<T>.addIfNotNull(t: T?) {
6364
if (t != null) add(t)
6465
}
6566

67+
suspend fun <T: Any> SequenceBuilder<in T>.yieldIfNotNull(t: T?) = if (t != null) yield(t) else Unit
68+
6669
fun <K, V> newHashMapWithExpectedSize(expectedSize: Int): HashMap<K, V> =
6770
HashMap(capacity(expectedSize))
6871

0 commit comments

Comments
 (0)