[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:
committed by
Space Team
parent
a3f0e429e3
commit
b476eee6e3
+3
-56
@@ -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
|
||||
|
||||
Vendored
+11
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user