[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?
|
callableReferenceAccess: FirCallableReferenceAccess?
|
||||||
): IrExpression? {
|
): IrExpression? {
|
||||||
val classSymbol = (qualifier.typeRef.coneType as? ConeClassLikeType)?.lookupTag?.toSymbol(session)
|
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()
|
if (callableReferenceAccess?.isBound == false) {
|
||||||
?: firCallableSymbol.receiverParameter?.typeRef?.coneTypeSafe<ConeClassLikeType>()?.lookupTag
|
return null
|
||||||
?: 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val irType = qualifier.typeRef.toIrType()
|
val irType = qualifier.typeRef.toIrType()
|
||||||
return qualifier.convertWithOffsets { startOffset, endOffset ->
|
return qualifier.convertWithOffsets { startOffset, endOffset ->
|
||||||
if (classSymbol != null) {
|
if (classSymbol != null) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.KtSourceElement
|
|||||||
import org.jetbrains.kotlin.fir.FirElement
|
import org.jetbrains.kotlin.fir.FirElement
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
|
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
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.diagnostics.ConeDiagnostic
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
|
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.builder.buildErrorExpression
|
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.FirNoReceiverExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
|
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
|
||||||
import org.jetbrains.kotlin.fir.references.FirReference
|
import org.jetbrains.kotlin.fir.references.*
|
||||||
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.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||||
@@ -162,3 +160,12 @@ fun FirExpression.unwrapSmartcastExpression(): FirExpression =
|
|||||||
is FirSmartCastExpression -> originalExpression
|
is FirSmartCastExpression -> originalExpression
|
||||||
else -> this
|
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::instanceProp, A.Companion)) return "Fail unbound 7"
|
||||||
if (!callContext(A::extProp, A.Companion)) return "Fail unbound 8"
|
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 instance = A::instance
|
||||||
val ext = A::ext
|
val ext = A::ext
|
||||||
val instanceProp = A::instanceProp
|
val instanceProp = A::instanceProp
|
||||||
|
|||||||
Reference in New Issue
Block a user