[FIR2IR] Use companion as bound receiver if expected type requires it

A::foo is preferably resolved as an unbound reference to A.foo, however
if A.Companion is a subtype of A and the expected type has one fewer
parameter than the type of the unbound reference, generate a bound
reference with the companion as receiver.

^KT-56519 Fixed
This commit is contained in:
Kirill Rakhman
2023-02-28 15:55:04 +01:00
committed by Space Team
parent fc96eb6d8d
commit 1f0e6321b0
15 changed files with 158 additions and 5 deletions
@@ -709,9 +709,12 @@ class CallAndReferenceGenerator(
val firCallableSymbol = resolvedReference.resolvedSymbol as FirCallableSymbol<*>
// Make sure the reference indeed refers to a member of that companion
val dispatchReceiverClassLookupTag = firCallableSymbol.dispatchReceiverClassLookupTagOrNull() ?: return null
val receiverClassLookupTag = firCallableSymbol.dispatchReceiverClassLookupTagOrNull()
?: firCallableSymbol.receiverParameter?.typeRef?.coneTypeSafe<ConeClassLikeType>()?.lookupTag
?: return null
val callableIsDefinitelyNotMemberOfCompanion = with(session.typeContext) {
!classSymbol.defaultType().anySuperTypeConstructor { it.typeConstructor() == dispatchReceiverClassLookupTag }
!classSymbol.defaultType().anySuperTypeConstructor { it.typeConstructor() == receiverClassLookupTag }
}
if (callableIsDefinitelyNotMemberOfCompanion) {
return null
@@ -734,10 +737,21 @@ class CallAndReferenceGenerator(
*/
val containingClass = classSymbol.getContainingClassLookupTag()?.toFirRegularClassSymbol(session) ?: return null
val callableIsDefinitelyMemberOfOuterClass = with(session.typeContext) {
containingClass.defaultType().anySuperTypeConstructor { it.typeConstructor() == dispatchReceiverClassLookupTag }
containingClass.defaultType().anySuperTypeConstructor { it.typeConstructor() == receiverClassLookupTag }
}
if (callableIsDefinitelyMemberOfOuterClass) {
return null
val expectedParameterCount = callableReferenceAccess.typeRef.coneType.typeArguments.size - 1
val declaration = firCallableSymbol.fir
val requiredParameterCount = ((declaration as? FirFunction)?.valueParameters?.size ?: 0) +
(if (declaration.dispatchReceiverType != null) 1 else 0) +
(if (declaration.receiverParameter != null) 1 else 0)
// However, if the number of parameters only match, if the receiver is bound, allow using the companion as receiver.
val boundCallableIsExpected = requiredParameterCount == expectedParameterCount + 1
if (!boundCallableIsExpected) {
return null
}
}
}
}
@@ -3668,6 +3668,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -3668,6 +3668,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -5,4 +5,4 @@ class A {
}
}
fun box() = (A.Companion::ok).let { it() }
fun box() = (A.Companion::ok).let { it() }
@@ -0,0 +1,69 @@
// !LANGUAGE: +ContextReceivers
// IGNORE_BACKEND_K1: JS, NATIVE
// SKIP_NODE_JS
open class A {
fun instance() = true
val instanceProp = true
companion object : A() {
fun companion() = true
val companionProp = true
}
}
fun A.ext() = true
val A.extProp get() = true
fun A.Companion.companionExt() = true
val A.Companion.companionExtProp get() = true
inline fun call(f: () -> Boolean) = f()
inline fun callExtension(f: A.() -> Boolean, receiver: A) = receiver.f()
inline fun callParameter(f: (A) -> Boolean, parameter: A) = f(parameter)
inline fun callContext(f: context(A) () -> Boolean, receiver: A) = f(receiver)
fun box(): String {
if (!call(A::instance)) return "Fail bound function 1"
if (!call(A.Companion::instance)) return "Fail bound function 2"
if (!call(A::companion)) return "Fail bound function 3"
if (!call(A.Companion::companion)) return "Fail bound function 4"
if (!call(A::ext)) return "Fail bound function 5"
if (!call(A.Companion::ext)) return "Fail bound function 6"
if (!call(A::companionExt)) return "Fail bound function 7"
if (!call(A.Companion::companionExt)) return "Fail bound function 8"
if (!call(A::instanceProp)) return "Fail bound prop 1"
if (!call(A.Companion::instanceProp)) return "Fail bound prop 2"
if (!call(A::companionProp)) return "Fail bound prop 3"
if (!call(A.Companion::companionProp)) return "Fail bound prop 4"
if (!call(A::extProp)) return "Fail bound prop 5"
if (!call(A.Companion::extProp)) return "Fail bound prop 6"
if (!call(A::companionExtProp)) return "Fail bound prop 7"
if (!call(A.Companion::companionExtProp)) return "Fail bound prop 8"
if (!callParameter(A::instance, A.Companion)) return "Fail unbound 1"
if (!callParameter(A::ext, A.Companion)) return "Fail unbound 2"
if (!callParameter(A::instanceProp, A.Companion)) return "Fail unbound 3"
if (!callParameter(A::extProp, A.Companion)) return "Fail unbound 4"
if (!callExtension(A::instance, A.Companion)) return "Fail unbound 5"
if (!callExtension(A::ext, A.Companion)) return "Fail unbound 6"
if (!callExtension(A::instanceProp, A.Companion)) return "Fail unbound 7"
if (!callExtension(A::extProp, A.Companion)) return "Fail unbound 8"
if (!callContext(A::instance, A.Companion)) return "Fail unbound 5"
if (!callContext(A::ext, A.Companion)) return "Fail unbound 6"
if (!callContext(A::instanceProp, A.Companion)) return "Fail unbound 7"
if (!callContext(A::extProp, A.Companion)) return "Fail unbound 8"
val instance = A::instance
val ext = A::ext
val instanceProp = A::instanceProp
val extProp = A::extProp
if (!callParameter(instance, A.Companion)) return "Fail unbound variable 1"
if (!callParameter(ext, A.Companion)) return "Fail unbound variable 2"
if (!callParameter(instanceProp, A.Companion)) return "Fail unbound variable 3"
if (!callParameter(extProp, A.Companion)) return "Fail unbound variable 4"
return "OK"
}
@@ -3524,6 +3524,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -3668,6 +3668,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -3086,6 +3086,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/dontShareReceiver.kt");
@@ -2354,6 +2354,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -2408,6 +2408,12 @@ public class FirJsCodegenBoxTestGenerated extends AbstractFirJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -2408,6 +2408,12 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -2408,6 +2408,12 @@ public class IrJsES6CodegenBoxTestGenerated extends AbstractIrJsES6CodegenBoxTes
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -2497,6 +2497,12 @@ public class K2NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTes
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -2466,6 +2466,12 @@ public class NativeCodegenBoxTestGenerated extends AbstractNativeCodegenBoxTest
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@Test
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@Test
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
@@ -2141,6 +2141,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiver.kt");
}
@TestMetadata("companionObjectReceiverInheritsFromOuter.kt")
public void testCompanionObjectReceiverInheritsFromOuter() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/companionObjectReceiverInheritsFromOuter.kt");
}
@TestMetadata("dontShareReceiver.kt")
public void testDontShareReceiver() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/bound/dontShareReceiver.kt");