FIR: Support callable references partially
^KT-32725 In Progress
This commit is contained in:
@@ -38,6 +38,7 @@ object StandardClassIds {
|
||||
val KProperty = "KProperty".reflectId()
|
||||
|
||||
fun byName(name: String) = name.baseId()
|
||||
fun reflectByName(name: String) = name.reflectId()
|
||||
|
||||
val primitiveArrayTypeByElementType: Map<ClassId, ClassId> = mutableMapOf<ClassId, ClassId>().apply {
|
||||
fun addPrimitive(id: ClassId) {
|
||||
@@ -55,4 +56,4 @@ object StandardClassIds {
|
||||
}
|
||||
|
||||
val elementTypeByPrimitiveArrayType = primitiveArrayTypeByElementType.map { (k, v) -> v to k }.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ package org.jetbrains.kotlin.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.FirMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccess
|
||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirExpressionStub
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedQualifierImpl
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference
|
||||
@@ -18,8 +18,7 @@ import org.jetbrains.kotlin.fir.references.impl.FirBackingFieldReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirErrorNamedReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirResolvedCallableReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStack
|
||||
import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.StoreNameReference
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver
|
||||
@@ -28,12 +27,17 @@ import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.phasedFir
|
||||
import org.jetbrains.kotlin.fir.resolve.typeForQualifier
|
||||
import org.jetbrains.kotlin.fir.resolve.typeFromCallee
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.*
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirLocalScope
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.FirTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
|
||||
|
||||
class FirCallResolver(
|
||||
@@ -217,6 +221,88 @@ class FirCallResolver(
|
||||
return resultExpression
|
||||
}
|
||||
|
||||
fun resolveCallableReference(callableReferenceAccess: FirCallableReferenceAccess, lhs: DoubleColonLHS?): FirCallableReferenceAccess {
|
||||
val resultCollector = ReferencesCandidateCollector(this, resolutionStageRunner)
|
||||
|
||||
val consumer =
|
||||
createCallableReferencesConsumerForLHS(
|
||||
callableReferenceAccess, lhs, resultCollector
|
||||
)
|
||||
|
||||
towerResolver.runResolver(consumer, implicitReceiverStack.receiversAsReversed())
|
||||
|
||||
val result = resultCollector.results.firstOrNull() ?: return callableReferenceAccess
|
||||
|
||||
val resultingType: ConeKotlinType = when (val fir = result.symbol.fir) {
|
||||
is FirSimpleFunction -> createKFunctionType(fir, lhs)
|
||||
is FirProperty -> createKPropertyType(fir)
|
||||
else -> ConeKotlinErrorType("Unknown callable kind: ${fir::class}")
|
||||
}
|
||||
|
||||
callableReferenceAccess.replaceTypeRef(FirResolvedTypeRefImpl(null, resultingType))
|
||||
|
||||
return callableReferenceAccess.transformCalleeReference(
|
||||
StoreNameReference,
|
||||
FirNamedReferenceWithCandidate(callableReferenceAccess.psi, callableReferenceAccess.calleeReference.name, result)
|
||||
)
|
||||
}
|
||||
|
||||
private fun createKPropertyType(fir: FirProperty): ConeKotlinType {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
private fun createKFunctionType(
|
||||
function: FirSimpleFunction,
|
||||
lhs: DoubleColonLHS?
|
||||
): ConeKotlinType {
|
||||
val receiverType = (lhs as? DoubleColonLHS.Type)?.type
|
||||
val parameterTypes = function.valueParameters.map {
|
||||
it.returnTypeRef.coneTypeSafe<ConeKotlinType>() ?: ConeKotlinErrorType("No type for parameter $it")
|
||||
}
|
||||
|
||||
return createFunctionalType(
|
||||
parameterTypes, receiverType = receiverType,
|
||||
rawReturnType = function.returnTypeRef.coneTypeSafe() ?: ConeKotlinErrorType("No type for return type of $this")
|
||||
)
|
||||
}
|
||||
|
||||
private fun createCallableReferencesConsumerForLHS(
|
||||
callableReferenceAccess: FirCallableReferenceAccess,
|
||||
lhs: DoubleColonLHS?,
|
||||
resultCollector: CandidateCollector
|
||||
): TowerDataConsumer {
|
||||
val name = callableReferenceAccess.calleeReference.name
|
||||
|
||||
return when (lhs) {
|
||||
is DoubleColonLHS.Expression, null -> createCallableReferencesConsumerForReceiver(
|
||||
name, resultCollector, callableReferenceAccess.explicitReceiver
|
||||
)
|
||||
is DoubleColonLHS.Type -> createCallableReferencesConsumerForReceiver(
|
||||
name, resultCollector,
|
||||
FirExpressionStub(callableReferenceAccess.psi).apply { replaceTypeRef(FirResolvedTypeRefImpl(null, lhs.type)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCallableReferencesConsumerForReceiver(
|
||||
name: Name,
|
||||
resultCollector: CandidateCollector,
|
||||
receiver: FirExpression?
|
||||
): TowerDataConsumer {
|
||||
val info = CallInfo(
|
||||
CallKind.CallableReference,
|
||||
receiver,
|
||||
emptyList(),
|
||||
false,
|
||||
emptyList(),
|
||||
session,
|
||||
file,
|
||||
transformer.components.container
|
||||
) { it.resultType }
|
||||
|
||||
return createCallableReferencesConsumer(session, name, info, this, resultCollector)
|
||||
}
|
||||
|
||||
private fun createResolvedNamedReference(
|
||||
namedReference: FirNamedReference,
|
||||
candidates: Collection<Candidate>,
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:Suppress("DuplicatedCode")
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.FirNamedReference
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.ConeStarProjection
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
|
||||
|
||||
sealed class DoubleColonLHS(val type: ConeKotlinType) {
|
||||
/**
|
||||
* [isObjectQualifier] is true iff the LHS of a callable reference is a qualified expression which references a named object.
|
||||
* Note that such LHS can be treated both as a type and as an expression, so special handling may be required.
|
||||
*
|
||||
* For example, if `Obj` is an object:
|
||||
*
|
||||
* Obj::class // object qualifier
|
||||
* test.Obj::class // object qualifier
|
||||
* (Obj)::class // not an object qualifier (can only be treated as an expression, not as a type)
|
||||
* { Obj }()::class // not an object qualifier
|
||||
*/
|
||||
class Expression(type: ConeKotlinType, val isObjectQualifier: Boolean) : DoubleColonLHS(type)
|
||||
|
||||
class Type(type: ConeKotlinType) : DoubleColonLHS(type)
|
||||
}
|
||||
|
||||
|
||||
// Returns true if this expression has the form "A<B>" which means it's a type on the LHS of a double colon expression
|
||||
internal val FirFunctionCall.hasExplicitValueArguments: Boolean
|
||||
get() = true // TODO: hasExplicitArgumentList || hasExplicitLambdaArguments
|
||||
|
||||
class FirDoubleColonExpressionResolver(
|
||||
private val session: FirSession
|
||||
) {
|
||||
|
||||
// Returns true if the expression is not a call expression without value arguments (such as "A<B>") or a qualified expression
|
||||
// which contains such call expression as one of its parts.
|
||||
// In this case it's pointless to attempt to type check an expression on the LHS in "A<B>::class", since "A<B>" certainly means a type.
|
||||
private fun FirExpression.canBeConsideredProperExpression(): Boolean {
|
||||
return when {
|
||||
this is FirQualifiedAccessExpression && explicitReceiver?.canBeConsideredProperExpression() != true -> false
|
||||
this is FirFunctionCall && !hasExplicitValueArguments -> false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirExpression.canBeConsideredProperType(): Boolean {
|
||||
return when {
|
||||
this is FirFunctionCall &&
|
||||
explicitReceiver?.canBeConsideredProperType() != false -> !hasExplicitValueArguments
|
||||
this is FirQualifiedAccessExpression &&
|
||||
explicitReceiver?.canBeConsideredProperType() != false &&
|
||||
calleeReference is FirNamedReference -> true
|
||||
this is FirResolvedQualifier -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldTryResolveLHSAsExpression(expression: FirCallableReferenceAccess): Boolean {
|
||||
val lhs = expression.explicitReceiver ?: return false
|
||||
return lhs.canBeConsideredProperExpression() /* && !expression.hasQuestionMarks */
|
||||
}
|
||||
|
||||
private fun shouldTryResolveLHSAsType(expression: FirCallableReferenceAccess): Boolean {
|
||||
val lhs = expression.explicitReceiver
|
||||
return lhs != null && lhs.canBeConsideredProperType()
|
||||
}
|
||||
|
||||
internal fun resolveDoubleColonLHS(doubleColonExpression: FirCallableReferenceAccess): DoubleColonLHS? {
|
||||
val resultForExpr = tryResolveLHS(doubleColonExpression, this::shouldTryResolveLHSAsExpression, this::resolveExpressionOnLHS)
|
||||
if (resultForExpr != null && !resultForExpr.isObjectQualifier) {
|
||||
return resultForExpr
|
||||
}
|
||||
|
||||
val resultForType = tryResolveLHS(doubleColonExpression, this::shouldTryResolveLHSAsType) { expression ->
|
||||
resolveTypeOnLHS(expression)
|
||||
}
|
||||
|
||||
if (resultForType != null) {
|
||||
if (resultForExpr != null && resultForType.type == resultForExpr.type) {
|
||||
// If we skipped an object expression result before and the type result is the same, this means that
|
||||
// there were no other classifier except that object that could win. We prefer to treat the LHS as an expression here,
|
||||
// to have a bound callable reference / class literal
|
||||
return resultForExpr
|
||||
}
|
||||
return resultForType
|
||||
}
|
||||
|
||||
// If the LHS could be resolved neither as an expression nor as a type, we should still type-check it to allow all diagnostics
|
||||
// to be reported and references to be resolved. For that, we commit one of the applicable traces here, preferring the expression
|
||||
return resultForExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if the LHS is definitely not an expression. Returns a non-null result if a resolution was attempted and led to
|
||||
* either a successful result or not.
|
||||
*/
|
||||
private fun <T : DoubleColonLHS> tryResolveLHS(
|
||||
doubleColonExpression: FirCallableReferenceAccess,
|
||||
criterion: (FirCallableReferenceAccess) -> Boolean,
|
||||
resolve: (FirExpression) -> T?
|
||||
): T? {
|
||||
val expression = doubleColonExpression.explicitReceiver ?: return null
|
||||
|
||||
if (!criterion(doubleColonExpression)) return null
|
||||
|
||||
return resolve(expression)
|
||||
}
|
||||
|
||||
private fun resolveExpressionOnLHS(expression: FirExpression): DoubleColonLHS.Expression? {
|
||||
val type = (expression.typeRef as? FirResolvedTypeRef)?.type ?: return null
|
||||
|
||||
if (expression is FirResolvedQualifier) {
|
||||
val firClass = session.firSymbolProvider
|
||||
.getClassLikeSymbolByFqName(expression.classId ?: return null)
|
||||
?.fir as? FirRegularClass
|
||||
?: return null
|
||||
|
||||
if (firClass.classKind == ClassKind.OBJECT) return DoubleColonLHS.Expression(type, isObjectQualifier = true)
|
||||
return null
|
||||
}
|
||||
|
||||
return DoubleColonLHS.Expression(type, isObjectQualifier = false)
|
||||
}
|
||||
|
||||
private fun resolveTypeOnLHS(
|
||||
expression: FirExpression
|
||||
): DoubleColonLHS.Type? {
|
||||
val resolvedExpression =
|
||||
expression as? FirResolvedQualifier
|
||||
?: return null
|
||||
|
||||
val firClass = session.firSymbolProvider
|
||||
.getClassLikeSymbolByFqName(resolvedExpression.classId ?: return null)
|
||||
// TODO: support type aliases
|
||||
?.fir as? FirRegularClass
|
||||
?: return null
|
||||
|
||||
val type = ConeClassTypeImpl(
|
||||
firClass.symbol.toLookupTag(),
|
||||
Array(firClass.typeParameters.size) { ConeStarProjection },
|
||||
isNullable = false // TODO: Use org.jetbrains.kotlin.psi.KtDoubleColonExpression.getHasQuestionMarks
|
||||
)
|
||||
|
||||
return DoubleColonLHS.Type(type)
|
||||
}
|
||||
}
|
||||
@@ -216,11 +216,13 @@ fun FirFunction<*>.constructFunctionalTypeRef(session: FirSession): FirResolvedT
|
||||
fun createFunctionalType(
|
||||
parameters: List<ConeKotlinType>,
|
||||
receiverType: ConeKotlinType?,
|
||||
rawReturnType: ConeKotlinType
|
||||
rawReturnType: ConeKotlinType,
|
||||
isKFunctionType: Boolean = false
|
||||
): ConeLookupTagBasedType {
|
||||
val receiverAndParameterTypes = listOfNotNull(receiverType) + parameters + listOf(rawReturnType)
|
||||
|
||||
val functionalTypeId = StandardClassIds.byName("Function${receiverAndParameterTypes.size - 1}")
|
||||
val postfix = "Function${receiverAndParameterTypes.size - 1}"
|
||||
val functionalTypeId = if (isKFunctionType) StandardClassIds.reflectByName("K$postfix") else StandardClassIds.byName(postfix)
|
||||
return ConeClassTypeImpl(ConeClassLikeLookupTagImpl(functionalTypeId), receiverAndParameterTypes.toTypedArray(), isNullable = false)
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ fun resolveArgumentExpression(
|
||||
typeProvider
|
||||
)
|
||||
// TODO:!
|
||||
is FirCallableReferenceAccess -> Unit
|
||||
is FirCallableReferenceAccess -> preprocessCallableReference(csBuilder, argument, expectedType, sink)
|
||||
// NB: FirCallableReferenceAccess should be checked earlier
|
||||
is FirQualifiedAccessExpression -> resolvePlainExpressionArgument(
|
||||
csBuilder,
|
||||
|
||||
@@ -43,4 +43,12 @@ sealed class CallKind {
|
||||
CheckReceivers.Extension
|
||||
)
|
||||
}
|
||||
|
||||
object CallableReference : CallKind() {
|
||||
override val resolutionSequence: List<ResolutionStage> = listOf(
|
||||
CheckVisibility,
|
||||
DiscriminateSynthetics,
|
||||
CreateFreshTypeVariableSubstitutorStage
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,20 @@ open class CandidateCollector(
|
||||
}
|
||||
}
|
||||
|
||||
class ReferencesCandidateCollector(components: BodyResolveComponents, resolutionStageRunner: ResolutionStageRunner) :
|
||||
CandidateCollector(components, resolutionStageRunner) {
|
||||
|
||||
val results = mutableListOf<Candidate>()
|
||||
|
||||
override fun consumeCandidate(group: Int, candidate: Candidate): CandidateApplicability {
|
||||
val applicability = resolutionStageRunner.processCandidate(candidate)
|
||||
if (applicability >= CandidateApplicability.SYNTHETIC_RESOLVED) {
|
||||
results.add(candidate)
|
||||
}
|
||||
return CandidateApplicability.SYNTHETIC_RESOLVED
|
||||
}
|
||||
}
|
||||
|
||||
// Collects properties that potentially could be invoke receivers, like 'propertyName()',
|
||||
// and initiates further invoke resolution by adding property-bound invoke consumers
|
||||
class InvokeReceiverCandidateCollector(
|
||||
|
||||
@@ -44,7 +44,8 @@ class CandidateFactory(
|
||||
|
||||
fun PostponedArgumentsAnalyzer.Context.addSubsystemFromExpression(expression: FirExpression) {
|
||||
when (expression) {
|
||||
is FirFunctionCall, is FirWhenExpression, is FirTryExpression -> (expression as FirResolvable).candidate()?.let { addOtherSystem(it.system.asReadOnlyStorage()) }
|
||||
is FirFunctionCall, is FirWhenExpression, is FirTryExpression, is FirCallableReferenceAccess ->
|
||||
(expression as FirResolvable).candidate()?.let { addOtherSystem(it.system.asReadOnlyStorage()) }
|
||||
is FirWrappedArgumentExpression -> addSubsystemFromExpression(expression.expression)
|
||||
is FirBlock -> expression.returnExpressions().forEach { addSubsystemFromExpression(it) }
|
||||
}
|
||||
|
||||
@@ -136,3 +136,19 @@ fun createSimpleConsumer(
|
||||
NoExplicitReceiverTowerDataConsumer(session, name, token, factory, resultCollector)
|
||||
}
|
||||
}
|
||||
|
||||
fun createCallableReferencesConsumer(
|
||||
session: FirSession,
|
||||
name: Name,
|
||||
callInfo: CallInfo,
|
||||
bodyResolveComponents: BodyResolveComponents,
|
||||
resultCollector: CandidateCollector
|
||||
): TowerDataConsumer {
|
||||
// TODO: Use SamePriorityConsumer
|
||||
return PrioritizedTowerDataConsumer(
|
||||
resultCollector,
|
||||
createSimpleConsumer(session, name, TowerScopeLevel.Token.Functions, callInfo, bodyResolveComponents, resultCollector)
|
||||
// TODO: Support properties
|
||||
// , createSimpleConsumer(session, name, TowerScopeLevel.Token.Properties, callInfo, bodyResolveComponents, resultCollector)
|
||||
)
|
||||
}
|
||||
|
||||
+18
-2
@@ -6,11 +6,14 @@
|
||||
package org.jetbrains.kotlin.fir.resolve.calls
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
|
||||
import org.jetbrains.kotlin.fir.resolve.constructType
|
||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.symbols.invoke
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassTypeImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
@@ -42,6 +45,19 @@ fun preprocessLambdaArgument(
|
||||
acceptLambdaAtoms(resolvedArgument)
|
||||
}
|
||||
|
||||
fun preprocessCallableReference(
|
||||
csBuilder: ConstraintSystemBuilder,
|
||||
argument: FirCallableReferenceAccess,
|
||||
expectedType: ConeKotlinType,
|
||||
sink: CheckerSink
|
||||
) {
|
||||
val type = argument.typeRef.coneTypeSafe<ConeKotlinType>() ?: return
|
||||
val candidate = argument.candidate() ?: return resolvePlainArgumentType(
|
||||
csBuilder, type, expectedType, sink, isReceiver = false, isSafeCall = false
|
||||
)
|
||||
val argumentType = candidate.substitutor.substituteOrSelf(type)
|
||||
resolvePlainArgumentType(csBuilder, argumentType, expectedType, sink, isReceiver = false, isSafeCall = false)
|
||||
}
|
||||
|
||||
val ConeKotlinType.isBuiltinFunctionalType: Boolean
|
||||
get() {
|
||||
@@ -77,14 +93,14 @@ private fun isFunctionalTypeWithReceiver(typeRef: FirTypeRef) =
|
||||
coneTypeSafe.lookupTag.classId.asString() == "kotlin/ExtensionFunctionType"
|
||||
}
|
||||
|
||||
private val ConeKotlinType.returnType: ConeKotlinType?
|
||||
val ConeKotlinType.returnType: ConeKotlinType?
|
||||
get() {
|
||||
require(this is ConeClassType)
|
||||
val projection = typeArguments.last()
|
||||
return (projection as? ConeTypedProjection)?.type
|
||||
}
|
||||
|
||||
private val ConeKotlinType.valueParameterTypes: List<ConeKotlinType?>
|
||||
val ConeKotlinType.valueParameterTypes: List<ConeKotlinType?>
|
||||
get() {
|
||||
require(this is ConeClassType)
|
||||
return typeArguments.dropLast(1).map {
|
||||
|
||||
+43
-5
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.fir.copy
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirResolvedCallableReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirResolvedRealCallableReferenceImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.Candidate
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.candidate
|
||||
import org.jetbrains.kotlin.fir.resolve.constructFunctionalTypeRef
|
||||
@@ -58,6 +60,34 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
).compose()
|
||||
}
|
||||
|
||||
override fun transformCallableReferenceAccess(
|
||||
callableReferenceAccess: FirCallableReferenceAccess,
|
||||
data: Nothing?
|
||||
): CompositeTransformResult<FirStatement> {
|
||||
|
||||
val calleeReference =
|
||||
callableReferenceAccess.calleeReference as? FirNamedReferenceWithCandidate ?: return callableReferenceAccess.compose()
|
||||
|
||||
val typeRef = callableReferenceAccess.typeRef as FirResolvedTypeRef
|
||||
|
||||
val initialType = calleeReference.candidate.substitutor.substituteOrSelf(typeRef.type)
|
||||
val finalType = finalSubstitutor.substituteOrSelf(initialType)
|
||||
|
||||
val resultType = typeRef.withReplacedConeType(finalType)
|
||||
callableReferenceAccess.replaceTypeRef(resultType)
|
||||
|
||||
return callableReferenceAccess.transformCalleeReference(
|
||||
StoreCalleeReference,
|
||||
FirResolvedRealCallableReferenceImpl(
|
||||
calleeReference.psi,
|
||||
calleeReference.name,
|
||||
calleeReference.candidateSymbol
|
||||
).apply {
|
||||
inferredTypeArguments.addAll(computeTypeArguments(calleeReference.candidate))
|
||||
}
|
||||
).compose()
|
||||
}
|
||||
|
||||
override fun transformVariableAssignment(
|
||||
variableAssignment: FirVariableAssignment,
|
||||
data: Nothing?
|
||||
@@ -80,9 +110,7 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
|
||||
val subCandidate = calleeReference.candidate
|
||||
val declaration = subCandidate.symbol.phasedFir as FirCallableMemberDeclaration<*>
|
||||
val newTypeParameters = declaration.typeParameters.map { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false) }
|
||||
.map { subCandidate.substitutor.substituteOrSelf(it) }
|
||||
.map { finalSubstitutor.substituteOrSelf(it) }
|
||||
val typeArguments = computeTypeArguments(subCandidate)
|
||||
.mapIndexed { index, type ->
|
||||
when (val argument = functionCall.typeArguments.getOrNull(index)) {
|
||||
is FirTypeProjectionWithVariance -> {
|
||||
@@ -119,7 +147,7 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
|
||||
return functionCall.copy(
|
||||
resultType = resultType,
|
||||
typeArguments = newTypeParameters,
|
||||
typeArguments = typeArguments,
|
||||
calleeReference = FirResolvedCallableReferenceImpl(
|
||||
calleeReference.psi,
|
||||
calleeReference.name,
|
||||
@@ -131,6 +159,16 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
|
||||
}
|
||||
|
||||
private fun computeTypeArguments(
|
||||
candidate: Candidate
|
||||
): List<ConeKotlinType> {
|
||||
val declaration = candidate.symbol.phasedFir as? FirCallableMemberDeclaration<*> ?: return emptyList()
|
||||
|
||||
return declaration.typeParameters.map { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false) }
|
||||
.map { candidate.substitutor.substituteOrSelf(it) }
|
||||
.map { finalSubstitutor.substituteOrSelf(it) }
|
||||
}
|
||||
|
||||
override fun transformAnonymousFunction(
|
||||
anonymousFunction: FirAnonymousFunction,
|
||||
data: Nothing?
|
||||
@@ -210,4 +248,4 @@ class FirCallCompletionResultsWriterTransformer(
|
||||
).compose()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-1
@@ -77,6 +77,13 @@ open class FirBodyResolveTransformer(
|
||||
return expressionsTransformer.transformFunctionCall(functionCall, data)
|
||||
}
|
||||
|
||||
override fun transformCallableReferenceAccess(
|
||||
callableReferenceAccess: FirCallableReferenceAccess,
|
||||
data: Any?
|
||||
): CompositeTransformResult<FirStatement> {
|
||||
return expressionsTransformer.transformCallableReferenceAccess(callableReferenceAccess, data)
|
||||
}
|
||||
|
||||
override fun transformBlock(block: FirBlock, data: Any?): CompositeTransformResult<FirStatement> {
|
||||
return expressionsTransformer.transformBlock(block, data)
|
||||
}
|
||||
@@ -217,4 +224,4 @@ open class FirBodyResolveTransformer(
|
||||
val result = this.transform<FirElement, D>(transformer, data)
|
||||
require(result.single === this) { "become ${result.single}: `${result.single.render()}`, was ${this}: `${this.render()}`" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-5
@@ -14,13 +14,11 @@ import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference
|
||||
import org.jetbrains.kotlin.fir.references.FirSuperReference
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirExplicitThisReference
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.candidate
|
||||
import org.jetbrains.kotlin.fir.resolve.constructType
|
||||
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.InvocationKindTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.typeFromCallee
|
||||
import org.jetbrains.kotlin.fir.resolve.withNullability
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.withReplacedConeType
|
||||
import org.jetbrains.kotlin.fir.symbols.StandardClassIds
|
||||
import org.jetbrains.kotlin.fir.symbols.invoke
|
||||
@@ -31,6 +29,7 @@ import org.jetbrains.kotlin.fir.types.impl.FirImplicitUnitTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.fir.visitors.CompositeTransformResult
|
||||
import org.jetbrains.kotlin.fir.visitors.compose
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstKind
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
|
||||
@@ -43,6 +42,7 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
|
||||
implicitReceiverStack,
|
||||
qualifiedResolver
|
||||
)
|
||||
private val doubleColonExpressionResolver: FirDoubleColonExpressionResolver = FirDoubleColonExpressionResolver(session)
|
||||
|
||||
private inline val builtinTypes: BuiltinTypes get() = session.builtinTypes
|
||||
|
||||
@@ -253,6 +253,24 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
|
||||
return result.compose()
|
||||
}
|
||||
|
||||
override fun transformCallableReferenceAccess(
|
||||
callableReferenceAccess: FirCallableReferenceAccess,
|
||||
data: Any?
|
||||
): CompositeTransformResult<FirStatement> {
|
||||
val transformedLHS =
|
||||
callableReferenceAccess.explicitReceiver?.transformSingle(this, noExpectedType)
|
||||
|
||||
val callableReferenceAccessWithTransformedLHS =
|
||||
if (transformedLHS != null)
|
||||
callableReferenceAccess.transformExplicitReceiver(StoreReceiver, transformedLHS)
|
||||
else
|
||||
callableReferenceAccess
|
||||
|
||||
val lhsResult = doubleColonExpressionResolver.resolveDoubleColonLHS(callableReferenceAccessWithTransformedLHS)
|
||||
|
||||
return callResolver.resolveCallableReference(callableReferenceAccessWithTransformedLHS, lhsResult).compose()
|
||||
}
|
||||
|
||||
override fun transformGetClassCall(getClassCall: FirGetClassCall, data: Any?): CompositeTransformResult<FirStatement> {
|
||||
val transformedGetClassCall = transformExpression(getClassCall, data).single as FirGetClassCall
|
||||
val kClassSymbol = ClassId.fromString("kotlin/reflect/KClass")(session.firSymbolProvider)
|
||||
@@ -349,4 +367,4 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
|
||||
internal fun <T> storeTypeFromCallee(access: T) where T : FirQualifiedAccess, T : FirExpression {
|
||||
access.resultType = callCompleter.typeFromCallee(access)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
fun <T, E> foo(x: (T) -> E) {}
|
||||
fun <T, E> foo2(x: (A, T) -> E) {}
|
||||
|
||||
class A {
|
||||
fun baz(x: String): Int = null!!
|
||||
}
|
||||
|
||||
fun bar(x: String): Int = null!!
|
||||
|
||||
fun main() {
|
||||
foo(::bar)
|
||||
foo(A()::baz)
|
||||
foo2(A::baz)
|
||||
}
|
||||
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
FILE: inferenceFromCallableReferenceType.kt
|
||||
public final fun <T, E> foo(x: R|kotlin/Function1<T, E>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun <T, E> foo2(x: R|kotlin/Function2<A, T, E>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun baz(x: R|kotlin/String|): R|kotlin/Int| {
|
||||
^baz when (lval <bangbang>: R|kotlin/Nothing?| = Null(null)) {
|
||||
==($subj$, Null(null)) -> {
|
||||
throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()
|
||||
}
|
||||
else -> {
|
||||
R|<local>/<bangbang>|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final fun bar(x: R|kotlin/String|): R|kotlin/Int| {
|
||||
^bar when (lval <bangbang>: R|kotlin/Nothing?| = Null(null)) {
|
||||
==($subj$, Null(null)) -> {
|
||||
throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()
|
||||
}
|
||||
else -> {
|
||||
R|<local>/<bangbang>|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun main(): R|kotlin/Unit| {
|
||||
R|/foo|<R|kotlin/String|, R|kotlin/Int|>(::R|/bar|)
|
||||
R|/foo|<R|kotlin/String|, R|kotlin/Int|>(R|/A.A|()::R|/A.baz|)
|
||||
R|/foo2|<R|kotlin/String|, R|kotlin/Int|>(Q|A|::R|/A.baz|)
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
fun foo(x: (String) -> Int) {}
|
||||
fun foo2(x: (A, String) -> Int) {}
|
||||
|
||||
class A {
|
||||
fun <T, E> baz(x: T): E = null!!
|
||||
}
|
||||
|
||||
fun <T, E> bar(x: T): E = null!!
|
||||
|
||||
fun main() {
|
||||
foo(::bar)
|
||||
foo(A()::baz)
|
||||
foo2(A::baz)
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
FILE: inferenceFromExpectedType.kt
|
||||
public final fun foo(x: R|kotlin/Function1<kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun foo2(x: R|kotlin/Function2<A, kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun <T, E> baz(x: R|T|): R|E| {
|
||||
^baz when (lval <bangbang>: R|kotlin/Nothing?| = Null(null)) {
|
||||
==($subj$, Null(null)) -> {
|
||||
throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()
|
||||
}
|
||||
else -> {
|
||||
R|<local>/<bangbang>|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
public final fun <T, E> bar(x: R|T|): R|E| {
|
||||
^bar when (lval <bangbang>: R|kotlin/Nothing?| = Null(null)) {
|
||||
==($subj$, Null(null)) -> {
|
||||
throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|()
|
||||
}
|
||||
else -> {
|
||||
R|<local>/<bangbang>|
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public final fun main(): R|kotlin/Unit| {
|
||||
R|/foo|(::R|/bar<kotlin/String, kotlin/Int>|)
|
||||
R|/foo|(R|/A.A|()::R|/A.baz<kotlin/String, kotlin/Int>|)
|
||||
R|/foo2|(Q|A|::R|/A.baz<kotlin/String, kotlin/Int>|)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
class A {
|
||||
fun bar(x: String): Int {}
|
||||
}
|
||||
|
||||
fun foo(x: (A, String) -> Int) {}
|
||||
|
||||
fun main() {
|
||||
foo(A::bar)
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
FILE: simpleClassReceiver.kt
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun bar(x: R|kotlin/String|): R|kotlin/Int| {
|
||||
}
|
||||
|
||||
}
|
||||
public final fun foo(x: R|kotlin/Function2<A, kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun main(): R|kotlin/Unit| {
|
||||
R|/foo|(Q|A|::R|/A.bar|)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
class A {
|
||||
fun bar(x: String): Int {}
|
||||
}
|
||||
|
||||
fun foo(x: (String) -> Int) {}
|
||||
|
||||
fun main() {
|
||||
val a = A()
|
||||
foo(a::bar)
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
FILE: simpleExpressionReceiver.kt
|
||||
public final class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final fun bar(x: R|kotlin/String|): R|kotlin/Int| {
|
||||
}
|
||||
|
||||
}
|
||||
public final fun foo(x: R|kotlin/Function1<kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun main(): R|kotlin/Unit| {
|
||||
lval a: R|A| = R|/A.A|()
|
||||
R|/foo|(R|<local>/a|::R|/A.bar|)
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fun foo(x: (String) -> Int) {}
|
||||
|
||||
fun bar(x: String): Int {}
|
||||
|
||||
fun main() {
|
||||
foo(::bar)
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
FILE: simpleNoReceiver.kt
|
||||
public final fun foo(x: R|kotlin/Function1<kotlin/String, kotlin/Int>|): R|kotlin/Unit| {
|
||||
}
|
||||
public final fun bar(x: R|kotlin/String|): R|kotlin/Int| {
|
||||
}
|
||||
public final fun main(): R|kotlin/Unit| {
|
||||
R|/foo|(::R|/bar|)
|
||||
}
|
||||
+38
@@ -28,6 +28,44 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/fir/resolve/testData/diagnostics/callableReferences")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class CallableReferences extends AbstractFirDiagnosticsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInCallableReferences() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/fir/resolve/testData/diagnostics/callableReferences"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("inferenceFromCallableReferenceType.kt")
|
||||
public void testInferenceFromCallableReferenceType() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromCallableReferenceType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inferenceFromExpectedType.kt")
|
||||
public void testInferenceFromExpectedType() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleClassReceiver.kt")
|
||||
public void testSimpleClassReceiver() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/simpleClassReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleExpressionReceiver.kt")
|
||||
public void testSimpleExpressionReceiver() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/simpleExpressionReceiver.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleNoReceiver.kt")
|
||||
public void testSimpleNoReceiver() throws Exception {
|
||||
runTest("compiler/fir/resolve/testData/diagnostics/callableReferences/simpleNoReceiver.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/fir/resolve/testData/diagnostics/j+k")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -90,6 +90,16 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun List<ConeKotlinType>.renderTypesSeparated() {
|
||||
for ((index, element) in this.withIndex()) {
|
||||
if (index > 0) {
|
||||
print(", ")
|
||||
}
|
||||
print(element.render())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun List<FirValueParameter>.renderParameters() {
|
||||
print("(")
|
||||
renderSeparated()
|
||||
@@ -806,6 +816,18 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() {
|
||||
}
|
||||
val symbol = resolvedCallableReference.resolvedSymbol
|
||||
print(symbol.render())
|
||||
|
||||
|
||||
if (resolvedCallableReference is FirResolvedRealCallableReference) {
|
||||
if (resolvedCallableReference.inferredTypeArguments.isNotEmpty()) {
|
||||
print("<")
|
||||
|
||||
resolvedCallableReference.inferredTypeArguments.renderTypesSeparated()
|
||||
|
||||
print(">")
|
||||
}
|
||||
}
|
||||
|
||||
if (isFakeOverride) {
|
||||
when (symbol) {
|
||||
is FirNamedFunctionSymbol -> {
|
||||
@@ -822,6 +844,10 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() {
|
||||
print("|")
|
||||
}
|
||||
|
||||
override fun visitResolvedRealCallableReference(resolvedRealCallableReference: FirResolvedRealCallableReference) {
|
||||
visitResolvedCallableReference(resolvedRealCallableReference)
|
||||
}
|
||||
|
||||
override fun visitThisReference(thisReference: FirThisReference) {
|
||||
print("this")
|
||||
val labelName = thisReference.labelName
|
||||
|
||||
Reference in New Issue
Block a user