FIR: Support callable references partially

^KT-32725 In Progress
This commit is contained in:
Denis Zharkov
2019-10-28 14:47:27 +03:00
parent cdb2bb2b45
commit 006e475a99
25 changed files with 623 additions and 24 deletions
@@ -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>,
@@ -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)
)
}
@@ -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 {
@@ -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()
}
}
}
@@ -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()}`" }
}
}
}
@@ -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)
}
}
}
@@ -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)
}
@@ -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|)
}
@@ -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)
}
@@ -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>|)
}
@@ -0,0 +1,9 @@
class A {
fun bar(x: String): Int {}
}
fun foo(x: (A, String) -> Int) {}
fun main() {
foo(A::bar)
}
@@ -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|)
}
@@ -0,0 +1,10 @@
class A {
fun bar(x: String): Int {}
}
fun foo(x: (String) -> Int) {}
fun main() {
val a = A()
foo(a::bar)
}
@@ -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|)
}
@@ -0,0 +1,7 @@
fun foo(x: (String) -> Int) {}
fun bar(x: String): Int {}
fun main() {
foo(::bar)
}
@@ -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|)
}
@@ -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