[FIR2IR] Simplify generation of bound callable reference receivers

Remove a bunch of custom logic to determine when a callable reference is
bound or not (and hence mustn't have a receiver). Instead, a helper
extension is introduced to determine when a callable reference is bound
by checking if dispatch/extensionReceiver is not FirNoReceiver and the
referenced member is not static.

#KT-57253 Fixed
This commit is contained in:
Kirill Rakhman
2023-03-10 17:18:51 +01:00
committed by Space Team
parent a3f0e429e3
commit b476eee6e3
3 changed files with 25 additions and 60 deletions
@@ -702,64 +702,11 @@ class CallAndReferenceGenerator(
callableReferenceAccess: FirCallableReferenceAccess?
): IrExpression? {
val classSymbol = (qualifier.typeRef.coneType as? ConeClassLikeType)?.lookupTag?.toSymbol(session)
if (callableReferenceAccess != null && classSymbol is FirRegularClassSymbol) {
val classId = qualifier.symbol?.fullyExpandedClass(session)?.classId
val classIdMatched = classSymbol.classId == classId
if (!classIdMatched) {
// Check whether we need get companion object as dispatch receiver
if (!classSymbol.fir.isCompanion || classSymbol.classId.outerClassId != classId) {
return null
}
val resolvedReference = callableReferenceAccess.calleeReference as FirResolvedNamedReference
val firCallableSymbol = resolvedReference.resolvedSymbol as FirCallableSymbol<*>
// Make sure the reference indeed refers to a member of that companion
val receiverClassLookupTag = firCallableSymbol.dispatchReceiverClassLookupTagOrNull()
?: firCallableSymbol.receiverParameter?.typeRef?.coneTypeSafe<ConeClassLikeType>()?.lookupTag
?: return null
val callableIsDefinitelyNotMemberOfCompanion = with(session.typeContext) {
!classSymbol.defaultType().anySuperTypeConstructor { it.typeConstructor() == receiverClassLookupTag }
}
if (callableIsDefinitelyNotMemberOfCompanion) {
return null
}
/*
* This check is supposed to distinguish cases when both companion and containing class have this function
*
* open class Base {
* fun foo() {}
* }
*
* class Some : Base() {
* companion object : Base() {
* fun bar()
* }
* }
*
* Some::foo // reference to Some.foo
* Some::bar // reference to Some.Companion.bar
*/
val containingClass = classSymbol.getContainingClassLookupTag()?.toFirRegularClassSymbol(session) ?: return null
val callableIsDefinitelyMemberOfOuterClass = with(session.typeContext) {
containingClass.defaultType().anySuperTypeConstructor { it.typeConstructor() == receiverClassLookupTag }
}
if (callableIsDefinitelyMemberOfOuterClass) {
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
}
}
}
if (callableReferenceAccess?.isBound == false) {
return null
}
val irType = qualifier.typeRef.toIrType()
return qualifier.convertWithOffsets { startOffset, endOffset ->
if (classSymbol != null) {
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.expressions.builder.buildErrorExpression
@@ -17,10 +18,7 @@ import org.jetbrains.kotlin.fir.expressions.impl.FirBlockImpl
import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
import org.jetbrains.kotlin.fir.references.FirReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.references.resolved
import org.jetbrains.kotlin.fir.references.toResolvedFunctionSymbol
import org.jetbrains.kotlin.fir.references.*
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
@@ -162,3 +160,12 @@ fun FirExpression.unwrapSmartcastExpression(): FirExpression =
is FirSmartCastExpression -> originalExpression
else -> this
}
/**
* A callable reference is bound iff
* - one of [dispatchReceiver] or [extensionReceiver] is **not** [FirNoReceiverExpression] and
* - it's not referring to a static member.
*/
val FirCallableReferenceAccess.isBound: Boolean
get() = (dispatchReceiver != FirNoReceiverExpression || extensionReceiver != FirNoReceiverExpression) &&
calleeReference.toResolvedCallableSymbol()?.isStatic != true
@@ -55,6 +55,17 @@ fun box(): String {
if (!callContext(A::instanceProp, A.Companion)) return "Fail unbound 7"
if (!callContext(A::extProp, A.Companion)) return "Fail unbound 8"
with (A) {
if (!call(::instance)) return "Fail implicit bound function 1"
if (!call(::companion)) return "Fail implicit bound function 2"
if (!call(::ext)) return "Fail implicit bound function 3"
if (!call(::companionExt)) return "Fail implicit bound function 4"
if (!call(::instanceProp)) return "Fail imlicit bound prop 5"
if (!call(::companionProp)) return "Fail imlicit bound prop 6"
if (!call(::extProp)) return "Fail imlicit bound prop 7"
if (!call(::companionExtProp)) return "Fail imlicit bound prop 8"
}
val instance = A::instance
val ext = A::ext
val instanceProp = A::instanceProp