diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt index e374d181d91..739f8b0aa93 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/symbols/StandardClassIds.kt @@ -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 = mutableMapOf().apply { fun addPrimitive(id: ClassId) { @@ -55,4 +56,4 @@ object StandardClassIds { } val elementTypeByPrimitiveArrayType = primitiveArrayTypeByElementType.map { (k, v) -> v to k }.toMap() -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index afe95acefef..80cd26d1616 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -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() ?: 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, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirDoubleColonExpressionResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirDoubleColonExpressionResolver.kt new file mode 100644 index 00000000000..559b215a960 --- /dev/null +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/FirDoubleColonExpressionResolver.kt @@ -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" 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") 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::class", since "A" 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 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) + } +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt index 9dc4d0809ac..af078771f40 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/ResolveUtils.kt @@ -216,11 +216,13 @@ fun FirFunction<*>.constructFunctionalTypeRef(session: FirSession): FirResolvedT fun createFunctionalType( parameters: List, 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) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt index f8a59fba0d6..9c8a6d16652 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/Arguments.kt @@ -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, diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt index 07b5cda678c..662bcbd70d0 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CallKind.kt @@ -43,4 +43,12 @@ sealed class CallKind { CheckReceivers.Extension ) } + + object CallableReference : CallKind() { + override val resolutionSequence: List = listOf( + CheckVisibility, + DiscriminateSynthetics, + CreateFreshTypeVariableSubstitutorStage + ) + } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateCollector.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateCollector.kt index 0344e6fc86b..f72a492cf76 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateCollector.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateCollector.kt @@ -66,6 +66,20 @@ open class CandidateCollector( } } +class ReferencesCandidateCollector(components: BodyResolveComponents, resolutionStageRunner: ResolutionStageRunner) : + CandidateCollector(components, resolutionStageRunner) { + + val results = mutableListOf() + + 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( diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt index fc8ddc9bd3f..f08e910a999 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/CandidateFactory.kt @@ -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) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConsumerFactories.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConsumerFactories.kt index 0154d525dd1..2b78fa20b5a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConsumerFactories.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/ConsumerFactories.kt @@ -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) + ) +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt index c7d7934368a..e45f5aa3257 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/PostponedArguments.kt @@ -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() ?: 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 +val ConeKotlinType.valueParameterTypes: List get() { require(this is ConeClassType) return typeArguments.dropLast(1).map { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt index 41040b52c5c..a86593106ad 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirCallCompletionResultsWriterTransformer.kt @@ -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 { + + 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 { + 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() } -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt index a471334cfc8..a6e6c1edc0d 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt @@ -77,6 +77,13 @@ open class FirBodyResolveTransformer( return expressionsTransformer.transformFunctionCall(functionCall, data) } + override fun transformCallableReferenceAccess( + callableReferenceAccess: FirCallableReferenceAccess, + data: Any? + ): CompositeTransformResult { + return expressionsTransformer.transformCallableReferenceAccess(callableReferenceAccess, data) + } + override fun transformBlock(block: FirBlock, data: Any?): CompositeTransformResult { return expressionsTransformer.transformBlock(block, data) } @@ -217,4 +224,4 @@ open class FirBodyResolveTransformer( val result = this.transform(transformer, data) require(result.single === this) { "become ${result.single}: `${result.single.render()}`, was ${this}: `${this.render()}`" } } -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 282dbefe7d5..3ef01af5cc0 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -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 { + 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 { 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 storeTypeFromCallee(access: T) where T : FirQualifiedAccess, T : FirExpression { access.resultType = callCompleter.typeFromCallee(access) } -} \ No newline at end of file +} diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromCallableReferenceType.kt b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromCallableReferenceType.kt new file mode 100644 index 00000000000..11c2bdceb65 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromCallableReferenceType.kt @@ -0,0 +1,14 @@ +fun foo(x: (T) -> E) {} +fun 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) +} diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromCallableReferenceType.txt b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromCallableReferenceType.txt new file mode 100644 index 00000000000..60da2371c93 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromCallableReferenceType.txt @@ -0,0 +1,39 @@ +FILE: inferenceFromCallableReferenceType.kt + public final fun foo(x: R|kotlin/Function1|): R|kotlin/Unit| { + } + public final fun foo2(x: R|kotlin/Function2|): R|kotlin/Unit| { + } + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun baz(x: R|kotlin/String|): R|kotlin/Int| { + ^baz when (lval : R|kotlin/Nothing?| = Null(null)) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + + } + + } + public final fun bar(x: R|kotlin/String|): R|kotlin/Int| { + ^bar when (lval : R|kotlin/Nothing?| = Null(null)) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + + } + public final fun main(): R|kotlin/Unit| { + R|/foo|(::R|/bar|) + R|/foo|(R|/A.A|()::R|/A.baz|) + R|/foo2|(Q|A|::R|/A.baz|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.kt b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.kt new file mode 100644 index 00000000000..846d9ac5fd0 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.kt @@ -0,0 +1,14 @@ +fun foo(x: (String) -> Int) {} +fun foo2(x: (A, String) -> Int) {} + +class A { + fun baz(x: T): E = null!! +} + +fun bar(x: T): E = null!! + +fun main() { + foo(::bar) + foo(A()::baz) + foo2(A::baz) +} diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.txt b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.txt new file mode 100644 index 00000000000..e664dd9b04a --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/inferenceFromExpectedType.txt @@ -0,0 +1,39 @@ +FILE: inferenceFromExpectedType.kt + public final fun foo(x: R|kotlin/Function1|): R|kotlin/Unit| { + } + public final fun foo2(x: R|kotlin/Function2|): R|kotlin/Unit| { + } + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun baz(x: R|T|): R|E| { + ^baz when (lval : R|kotlin/Nothing?| = Null(null)) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + + } + + } + public final fun bar(x: R|T|): R|E| { + ^bar when (lval : R|kotlin/Nothing?| = Null(null)) { + ==($subj$, Null(null)) -> { + throw R|kotlin/KotlinNullPointerException.KotlinNullPointerException|() + } + else -> { + R|/| + } + } + + } + public final fun main(): R|kotlin/Unit| { + R|/foo|(::R|/bar|) + R|/foo|(R|/A.A|()::R|/A.baz|) + R|/foo2|(Q|A|::R|/A.baz|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleClassReceiver.kt b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleClassReceiver.kt new file mode 100644 index 00000000000..a900eca189c --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleClassReceiver.kt @@ -0,0 +1,9 @@ +class A { + fun bar(x: String): Int {} +} + +fun foo(x: (A, String) -> Int) {} + +fun main() { + foo(A::bar) +} diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleClassReceiver.txt b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleClassReceiver.txt new file mode 100644 index 00000000000..6367d064cfa --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleClassReceiver.txt @@ -0,0 +1,15 @@ +FILE: simpleClassReceiver.kt + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun bar(x: R|kotlin/String|): R|kotlin/Int| { + } + + } + public final fun foo(x: R|kotlin/Function2|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + R|/foo|(Q|A|::R|/A.bar|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleExpressionReceiver.kt b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleExpressionReceiver.kt new file mode 100644 index 00000000000..9f0ff9a3207 --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleExpressionReceiver.kt @@ -0,0 +1,10 @@ +class A { + fun bar(x: String): Int {} +} + +fun foo(x: (String) -> Int) {} + +fun main() { + val a = A() + foo(a::bar) +} diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleExpressionReceiver.txt b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleExpressionReceiver.txt new file mode 100644 index 00000000000..9a0613edc5b --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleExpressionReceiver.txt @@ -0,0 +1,16 @@ +FILE: simpleExpressionReceiver.kt + public final class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public final fun bar(x: R|kotlin/String|): R|kotlin/Int| { + } + + } + public final fun foo(x: R|kotlin/Function1|): R|kotlin/Unit| { + } + public final fun main(): R|kotlin/Unit| { + lval a: R|A| = R|/A.A|() + R|/foo|(R|/a|::R|/A.bar|) + } diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleNoReceiver.kt b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleNoReceiver.kt new file mode 100644 index 00000000000..98c61c00fdb --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleNoReceiver.kt @@ -0,0 +1,7 @@ +fun foo(x: (String) -> Int) {} + +fun bar(x: String): Int {} + +fun main() { + foo(::bar) +} diff --git a/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleNoReceiver.txt b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleNoReceiver.txt new file mode 100644 index 00000000000..6d657c9c61b --- /dev/null +++ b/compiler/fir/resolve/testData/diagnostics/callableReferences/simpleNoReceiver.txt @@ -0,0 +1,8 @@ +FILE: simpleNoReceiver.kt + public final fun foo(x: R|kotlin/Function1|): 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|) + } diff --git a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 340b41e57c9..c91b54d36f4 100644 --- a/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/resolve/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -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) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt index 8320d70a2bf..6afac6b068c 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt @@ -90,6 +90,16 @@ class FirRenderer(builder: StringBuilder) : FirVisitorVoid() { } } + private fun List.renderTypesSeparated() { + for ((index, element) in this.withIndex()) { + if (index > 0) { + print(", ") + } + print(element.render()) + } + } + + private fun List.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