FIR: Support static methods in callable references

^KT-32725 In Progress
This commit is contained in:
Denis Zharkov
2019-10-18 14:36:57 +03:00
parent 937ea3d8a0
commit 38ab6521aa
7 changed files with 184 additions and 7 deletions
@@ -284,17 +284,42 @@ class FirCallResolver(
name, resultCollector, callableReferenceAccess.explicitReceiver, expectedType, outerConstraintSystemBuilder, name, resultCollector, callableReferenceAccess.explicitReceiver, expectedType, outerConstraintSystemBuilder,
lhs lhs
) )
is DoubleColonLHS.Type -> createCallableReferencesConsumerForReceiver( is DoubleColonLHS.Type -> createCallableReferencesConsumerForReceivers(
name, name,
resultCollector, resultCollector,
FirExpressionStub(callableReferenceAccess.psi).apply { replaceTypeRef(FirResolvedTypeRefImpl(null, lhs.type)) },
expectedType, expectedType,
outerConstraintSystemBuilder, outerConstraintSystemBuilder,
lhs lhs,
FirExpressionStub(callableReferenceAccess.psi).apply { replaceTypeRef(FirResolvedTypeRefImpl(null, lhs.type)) },
callableReferenceAccess.explicitReceiver
) )
} }
} }
private fun createCallableReferencesConsumerForReceivers(
name: Name,
resultCollector: CandidateCollector,
expectedType: ConeKotlinType?,
outerConstraintSystemBuilder: ConstraintSystemBuilder?,
lhs: DoubleColonLHS?,
vararg receivers: FirExpression?
): TowerDataConsumer {
if (receivers.size == 1) {
return createCallableReferencesConsumerForReceiver(
name, resultCollector, receivers[0], expectedType, outerConstraintSystemBuilder, lhs
)
}
return PrioritizedTowerDataConsumer(
resultCollector,
*Array(receivers.size) { index ->
createCallableReferencesConsumerForReceiver(
name, resultCollector, receivers[index], expectedType, outerConstraintSystemBuilder, lhs
)
}
)
}
private fun createCallableReferencesConsumerForReceiver( private fun createCallableReferencesConsumerForReceiver(
name: Name, name: Name,
resultCollector: CandidateCollector, resultCollector: CandidateCollector,
@@ -177,10 +177,14 @@ internal object CheckCallableReferenceExpectedType : CheckerStage() {
override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) { override suspend fun check(candidate: Candidate, sink: CheckerSink, callInfo: CallInfo) {
val outerCsBuilder = callInfo.outerCSBuilder ?: return val outerCsBuilder = callInfo.outerCSBuilder ?: return
val expectedType = callInfo.expectedType ?: return val expectedType = callInfo.expectedType ?: return
val lhs = callInfo.lhs
val resultingReceiverType = when (callInfo.lhs) {
is DoubleColonLHS.Type -> callInfo.lhs.type.takeIf { callInfo.explicitReceiver !is FirResolvedQualifier }
else -> null
}
val resultingType: ConeKotlinType = when (val fir = candidate.symbol.fir) { val resultingType: ConeKotlinType = when (val fir = candidate.symbol.fir) {
is FirSimpleFunction -> createKFunctionType(fir, lhs) is FirSimpleFunction -> createKFunctionType(fir, resultingReceiverType)
is FirProperty -> createKPropertyType(fir) is FirProperty -> createKPropertyType(fir)
else -> ConeKotlinErrorType("Unknown callable kind: ${fir::class}") else -> ConeKotlinErrorType("Unknown callable kind: ${fir::class}")
}.let(candidate.substitutor::substituteOrSelf) }.let(candidate.substitutor::substituteOrSelf)
@@ -212,9 +216,8 @@ private fun createKPropertyType(fir: FirProperty): ConeKotlinType {
private fun createKFunctionType( private fun createKFunctionType(
function: FirSimpleFunction, function: FirSimpleFunction,
lhs: DoubleColonLHS? receiverType: ConeKotlinType?
): ConeKotlinType { ): ConeKotlinType {
val receiverType = (lhs as? DoubleColonLHS.Type)?.type
val parameterTypes = function.valueParameters.map { val parameterTypes = function.valueParameters.map {
it.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: ConeKotlinErrorType("No type for parameter $it") it.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: ConeKotlinErrorType("No type for parameter $it")
} }
@@ -0,0 +1,43 @@
// FILE: JavaClass.java
public class JavaClass {
public static int bar(String x) { return 0; }
public int bar(CharSequence x) { return 0; }
}
// FILE: main.kt
class KotlinClass : JavaClass() {
fun baz(x: CharSequence): Int = 1
companion object {
fun baz(x: String): Int = 1
}
}
class KotlinClass2 : JavaClass() {
override fun bar(x: CharSequence): Int = 1
companion object {
fun bar(x: String): Int = 1
}
}
fun foo1(x: (String) -> Int) {}
fun foo2(x: (KotlinClass, CharSequence) -> Int) {}
fun foo3(x: (KotlinClass, CharSequence) -> Int) {}
fun foo3(x: (String) -> Int) {}
fun main() {
foo1(KotlinClass::baz)
foo2(KotlinClass::baz)
foo3(KotlinClass::baz)
foo1(KotlinClass::bar)
foo2(KotlinClass::bar)
foo3(KotlinClass::bar)
foo1(KotlinClass2::bar)
foo2(KotlinClass2::bar)
foo3(KotlinClass2::bar)
}
@@ -0,0 +1,62 @@
FILE: main.kt
public final class KotlinClass : R|JavaClass| {
public constructor(): R|KotlinClass| {
super<R|JavaClass|>()
}
public final fun baz(x: R|kotlin/CharSequence|): R|kotlin/Int| {
^baz Int(1)
}
public final companion object Companion : R|kotlin/Any| {
private constructor(): R|KotlinClass.Companion| {
super<R|kotlin/Any|>()
}
public final fun baz(x: R|kotlin/String|): R|kotlin/Int| {
^baz Int(1)
}
}
}
public final class KotlinClass2 : R|JavaClass| {
public constructor(): R|KotlinClass2| {
super<R|JavaClass|>()
}
public final override fun bar(x: R|kotlin/CharSequence|): R|kotlin/Int| {
^bar Int(1)
}
public final companion object Companion : R|kotlin/Any| {
private constructor(): R|KotlinClass2.Companion| {
super<R|kotlin/Any|>()
}
public final fun bar(x: R|kotlin/String|): R|kotlin/Int| {
^bar Int(1)
}
}
}
public final fun foo1(x: R|kotlin/Function1<kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun foo2(x: R|kotlin/Function2<KotlinClass, kotlin/CharSequence, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun foo3(x: R|kotlin/Function2<KotlinClass, kotlin/CharSequence, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun foo3(x: R|kotlin/Function1<kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun main(): R|kotlin/Unit| {
R|/foo1|(Q|KotlinClass|::R|/KotlinClass.Companion.baz|)
R|/foo2|(Q|KotlinClass|::R|/KotlinClass.baz|)
<Ambiguity: foo3, [/foo3, /foo3]>#(Q|KotlinClass|::baz#)
R|/foo1|(Q|KotlinClass|::R|/JavaClass.bar|)
R|/foo2|(Q|KotlinClass|::R|/JavaClass.bar|)
<Ambiguity: foo3, [/foo3, /foo3]>#(Q|KotlinClass|::bar#)
R|/foo1|(Q|KotlinClass2|::R|/JavaClass.bar|)
<Inapplicable(INAPPLICABLE): [/foo2]>#(Q|KotlinClass2|::bar#)
R|/foo3|(Q|KotlinClass2|::R|/JavaClass.bar|)
}
@@ -0,0 +1,20 @@
// FILE: JavaClass.java
public class JavaClass {
public static int bar(String x) { return 0; }
public int bar(CharSequence x) { return 0; }
}
// FILE: main.kt
fun foo1(x: (String) -> Int) {}
fun foo2(x: (JavaClass, CharSequence) -> Int) {}
fun foo3(x: (JavaClass, CharSequence) -> Int) {}
fun foo3(x: (String) -> Int) {}
fun main() {
foo1(JavaClass::bar)
foo2(JavaClass::bar)
foo3(JavaClass::bar)
}
@@ -0,0 +1,14 @@
FILE: main.kt
public final fun foo1(x: R|kotlin/Function1<kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun foo2(x: R|kotlin/Function2<JavaClass, kotlin/CharSequence, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun foo3(x: R|kotlin/Function2<JavaClass, kotlin/CharSequence, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun foo3(x: R|kotlin/Function1<kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
}
public final fun main(): R|kotlin/Unit| {
R|/foo1|(Q|JavaClass|::R|/JavaClass.bar|)
R|/foo2|(Q|JavaClass|::R|/JavaClass.bar|)
<Ambiguity: foo3, [/foo3, /foo3]>#(Q|JavaClass|::bar#)
}
@@ -40,6 +40,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/diagnostics/callableReferences"), Pattern.compile("^(.+)\\.kt$"), true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/diagnostics/callableReferences"), Pattern.compile("^(.+)\\.kt$"), true);
} }
@TestMetadata("companions.kt")
public void testCompanions() throws Exception {
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/companions.kt");
}
@TestMetadata("differentLevels.kt") @TestMetadata("differentLevels.kt")
public void testDifferentLevels() throws Exception { public void testDifferentLevels() throws Exception {
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/differentLevels.kt"); runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/differentLevels.kt");
@@ -55,6 +60,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.kt"); runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.kt");
} }
@TestMetadata("javaStatic.kt")
public void testJavaStatic() throws Exception {
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/javaStatic.kt");
}
@TestMetadata("manyCandidatesInference.kt") @TestMetadata("manyCandidatesInference.kt")
public void testManyCandidatesInference() throws Exception { public void testManyCandidatesInference() throws Exception {
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/manyCandidatesInference.kt"); runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/manyCandidatesInference.kt");