Extract CallableReferencesCandidateFactory into separate file
This commit is contained in:
-383
@@ -139,389 +139,6 @@ private fun ConstraintSystemOperation.addReceiverConstraint(
|
||||
addSubtypeConstraint(receiverType, expectedType, position)
|
||||
}
|
||||
|
||||
class CallableReferencesCandidateFactory(
|
||||
val argument: CallableReferenceKotlinCallArgument,
|
||||
val callComponents: KotlinCallComponents,
|
||||
val scopeTower: ImplicitScopeTower,
|
||||
val compatibilityChecker: ((ConstraintSystemOperation) -> Unit) -> Unit,
|
||||
val expectedType: UnwrappedType?,
|
||||
private val csBuilder: ConstraintSystemOperation,
|
||||
private val resolutionCallbacks: KotlinResolutionCallbacks
|
||||
) : CandidateFactory<CallableReferenceCandidate> {
|
||||
|
||||
fun createCallableProcessor(explicitReceiver: DetailedReceiver?) =
|
||||
createCallableReferenceProcessor(scopeTower, argument.rhsName, this, explicitReceiver)
|
||||
|
||||
override fun createCandidate(
|
||||
towerCandidate: CandidateWithBoundDispatchReceiver,
|
||||
explicitReceiverKind: ExplicitReceiverKind,
|
||||
extensionReceiver: ReceiverValueWithSmartCastInfo?
|
||||
): CallableReferenceCandidate {
|
||||
|
||||
val dispatchCallableReceiver =
|
||||
towerCandidate.dispatchReceiver?.let { toCallableReceiver(it, explicitReceiverKind == DISPATCH_RECEIVER) }
|
||||
val extensionCallableReceiver = extensionReceiver?.let { toCallableReceiver(it, explicitReceiverKind == EXTENSION_RECEIVER) }
|
||||
val candidateDescriptor = towerCandidate.descriptor
|
||||
val diagnostics = SmartList<KotlinCallDiagnostic>()
|
||||
|
||||
val (reflectionCandidateType, callableReferenceAdaptation) = buildReflectionType(
|
||||
candidateDescriptor,
|
||||
dispatchCallableReceiver,
|
||||
extensionCallableReceiver,
|
||||
expectedType,
|
||||
callComponents.builtIns
|
||||
)
|
||||
|
||||
fun createReferenceCandidate(): CallableReferenceCandidate =
|
||||
CallableReferenceCandidate(
|
||||
candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
|
||||
explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation, diagnostics
|
||||
)
|
||||
|
||||
if (callComponents.statelessCallbacks.isHiddenInResolution(candidateDescriptor, argument, resolutionCallbacks)) {
|
||||
diagnostics.add(HiddenDescriptor)
|
||||
return createReferenceCandidate()
|
||||
}
|
||||
|
||||
if (needCompatibilityResolveForCallableReference(callableReferenceAdaptation, candidateDescriptor)) {
|
||||
markCandidateForCompatibilityResolve(diagnostics)
|
||||
}
|
||||
|
||||
if (callableReferenceAdaptation != null && expectedType != null && hasNonTrivialAdaptation(callableReferenceAdaptation)) {
|
||||
if (!expectedType.isFunctionType && !expectedType.isSuspendFunctionType) { // expectedType has some reflection type
|
||||
diagnostics.add(AdaptedCallableReferenceIsUsedWithReflection(argument))
|
||||
}
|
||||
}
|
||||
|
||||
if (callableReferenceAdaptation != null &&
|
||||
callableReferenceAdaptation.defaults != 0 &&
|
||||
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType)
|
||||
) {
|
||||
diagnostics.add(CallableReferencesDefaultArgumentUsed(argument, candidateDescriptor, callableReferenceAdaptation.defaults))
|
||||
}
|
||||
|
||||
if (candidateDescriptor !is CallableMemberDescriptor) {
|
||||
return CallableReferenceCandidate(
|
||||
candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
|
||||
explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation,
|
||||
listOf(NotCallableMemberReference(argument, candidateDescriptor))
|
||||
)
|
||||
}
|
||||
|
||||
diagnostics.addAll(towerCandidate.diagnostics)
|
||||
// todo smartcast on receiver diagnostic and CheckInstantiationOfAbstractClass
|
||||
|
||||
compatibilityChecker {
|
||||
if (it.hasContradiction) return@compatibilityChecker
|
||||
|
||||
val (_, visibilityError) = it.checkCallableReference(
|
||||
argument, dispatchCallableReceiver, extensionCallableReceiver, candidateDescriptor,
|
||||
reflectionCandidateType, expectedType, scopeTower.lexicalScope.ownerDescriptor
|
||||
)
|
||||
|
||||
diagnostics.addIfNotNull(visibilityError)
|
||||
|
||||
if (it.hasContradiction) diagnostics.add(
|
||||
CallableReferenceNotCompatible(
|
||||
argument,
|
||||
candidateDescriptor,
|
||||
expectedType,
|
||||
reflectionCandidateType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return createReferenceCandidate()
|
||||
}
|
||||
|
||||
private fun needCompatibilityResolveForCallableReference(
|
||||
callableReferenceAdaptation: CallableReferenceAdaptation?,
|
||||
candidate: CallableDescriptor
|
||||
): Boolean {
|
||||
// KT-13934: reference to companion object member via class name
|
||||
if (candidate.containingDeclaration.isCompanionObject() && argument.lhsResult is LHSResult.Type) return true
|
||||
|
||||
if (callableReferenceAdaptation == null) return false
|
||||
|
||||
return hasNonTrivialAdaptation(callableReferenceAdaptation)
|
||||
}
|
||||
|
||||
private fun hasNonTrivialAdaptation(callableReferenceAdaptation: CallableReferenceAdaptation) =
|
||||
callableReferenceAdaptation.defaults != 0 ||
|
||||
callableReferenceAdaptation.suspendConversionStrategy != SuspendConversionStrategy.NO_CONVERSION ||
|
||||
callableReferenceAdaptation.coercionStrategy != CoercionStrategy.NO_COERCION ||
|
||||
callableReferenceAdaptation.mappedArguments.values.any { it is ResolvedCallArgument.VarargArgument }
|
||||
|
||||
private enum class VarargMappingState {
|
||||
UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY
|
||||
}
|
||||
|
||||
private fun getCallableReferenceAdaptation(
|
||||
descriptor: FunctionDescriptor,
|
||||
expectedType: UnwrappedType?,
|
||||
unboundReceiverCount: Int,
|
||||
builtins: KotlinBuiltIns
|
||||
): CallableReferenceAdaptation? {
|
||||
if (callComponents.languageVersionSettings.apiVersion < ApiVersion.KOTLIN_1_4) return null
|
||||
|
||||
if (expectedType == null) return null
|
||||
|
||||
// Do not adapt references against KCallable type as it's impossible to map defaults/vararg to absent parameters of KCallable
|
||||
if (ReflectionTypes.hasKCallableTypeFqName(expectedType)) return null
|
||||
|
||||
val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null
|
||||
|
||||
val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount
|
||||
if (expectedArgumentCount < 0) return null
|
||||
|
||||
val fakeArguments = createFakeArgumentsForReference(descriptor, expectedArgumentCount, inputOutputTypes, unboundReceiverCount)
|
||||
val argumentMapping =
|
||||
callComponents.argumentsToParametersMapper.mapArguments(fakeArguments, externalArgument = null, descriptor = descriptor)
|
||||
if (argumentMapping.diagnostics.any { !it.candidateApplicability.isSuccess }) return null
|
||||
|
||||
/**
|
||||
* (A, B, C) -> Unit
|
||||
* fun foo(a: A, b: B = B(), vararg c: C)
|
||||
*/
|
||||
var defaults = 0
|
||||
var varargMappingState = VarargMappingState.UNMAPPED
|
||||
val mappedArguments = linkedMapOf<ValueParameterDescriptor, ResolvedCallArgument>()
|
||||
val mappedVarargElements = linkedMapOf<ValueParameterDescriptor, MutableList<KotlinCallArgument>>()
|
||||
val mappedArgumentTypes = arrayOfNulls<KotlinType?>(fakeArguments.size)
|
||||
|
||||
for ((valueParameter, resolvedArgument) in argumentMapping.parameterToCallArgumentMap) {
|
||||
for (fakeArgument in resolvedArgument.arguments) {
|
||||
val index = (fakeArgument as FakeKotlinCallArgumentForCallableReference).index
|
||||
val substitutedParameter = descriptor.valueParameters.getOrNull(valueParameter.index) ?: continue
|
||||
|
||||
val mappedArgument: KotlinType?
|
||||
if (substitutedParameter.isVararg) {
|
||||
val (varargType, newVarargMappingState) = varargParameterTypeByExpectedParameter(
|
||||
inputOutputTypes.inputTypes[index + unboundReceiverCount],
|
||||
substitutedParameter,
|
||||
varargMappingState,
|
||||
builtins
|
||||
)
|
||||
varargMappingState = newVarargMappingState
|
||||
mappedArgument = varargType
|
||||
|
||||
when (newVarargMappingState) {
|
||||
VarargMappingState.MAPPED_WITH_ARRAY -> {
|
||||
// If we've already mapped an argument to this value parameter, it'll always be a type mismatch.
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.SimpleArgument(fakeArgument)
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_PLAIN_ARGS -> {
|
||||
mappedVarargElements.getOrPut(valueParameter) { ArrayList() }.add(fakeArgument)
|
||||
}
|
||||
VarargMappingState.UNMAPPED -> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mappedArgument = substitutedParameter.type
|
||||
mappedArguments[valueParameter] = resolvedArgument
|
||||
}
|
||||
|
||||
mappedArgumentTypes[index] = mappedArgument
|
||||
}
|
||||
if (resolvedArgument == ResolvedCallArgument.DefaultArgument) {
|
||||
defaults++
|
||||
mappedArguments[valueParameter] = resolvedArgument
|
||||
}
|
||||
}
|
||||
if (mappedArgumentTypes.any { it == null }) return null
|
||||
|
||||
for ((valueParameter, varargElements) in mappedVarargElements) {
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.VarargArgument(varargElements)
|
||||
}
|
||||
|
||||
for (valueParameter in descriptor.valueParameters) {
|
||||
if (valueParameter.isVararg && valueParameter !in mappedArguments) {
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.VarargArgument(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
// lower(Unit!) = Unit
|
||||
val returnExpectedType = inputOutputTypes.outputType
|
||||
|
||||
val coercion =
|
||||
if (returnExpectedType.isUnit() && descriptor.returnType?.isUnit() == false)
|
||||
CoercionStrategy.COERCION_TO_UNIT
|
||||
else
|
||||
CoercionStrategy.NO_COERCION
|
||||
|
||||
val adaptedArguments =
|
||||
if (ReflectionTypes.isBaseTypeForNumberedReferenceTypes(expectedType))
|
||||
emptyMap()
|
||||
else
|
||||
mappedArguments
|
||||
|
||||
val suspendConversionStrategy =
|
||||
if (!descriptor.isSuspend && expectedType.isSuspendFunctionType) {
|
||||
SuspendConversionStrategy.SUSPEND_CONVERSION
|
||||
} else {
|
||||
SuspendConversionStrategy.NO_CONVERSION
|
||||
}
|
||||
|
||||
return CallableReferenceAdaptation(
|
||||
@Suppress("UNCHECKED_CAST") (mappedArgumentTypes as Array<KotlinType>),
|
||||
coercion, defaults,
|
||||
adaptedArguments,
|
||||
suspendConversionStrategy
|
||||
)
|
||||
}
|
||||
|
||||
private fun createFakeArgumentsForReference(
|
||||
descriptor: FunctionDescriptor,
|
||||
expectedArgumentCount: Int,
|
||||
inputOutputTypes: InputOutputTypes,
|
||||
unboundReceiverCount: Int
|
||||
): List<FakeKotlinCallArgumentForCallableReference> {
|
||||
var afterVararg = false
|
||||
var varargComponentType: UnwrappedType? = null
|
||||
var vararg = false
|
||||
return (0 until expectedArgumentCount).map { index ->
|
||||
val inputType = inputOutputTypes.inputTypes.getOrNull(index + unboundReceiverCount)
|
||||
if (vararg && varargComponentType != inputType) {
|
||||
afterVararg = true
|
||||
}
|
||||
|
||||
val valueParameter = descriptor.valueParameters.getOrNull(index)
|
||||
val name =
|
||||
if (afterVararg && valueParameter?.declaresDefaultValue() == true)
|
||||
valueParameter.name
|
||||
else
|
||||
null
|
||||
|
||||
if (valueParameter?.isVararg == true) {
|
||||
varargComponentType = inputType
|
||||
vararg = true
|
||||
}
|
||||
FakeKotlinCallArgumentForCallableReference(index, name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun varargParameterTypeByExpectedParameter(
|
||||
expectedParameterType: KotlinType,
|
||||
substitutedParameter: ValueParameterDescriptor,
|
||||
varargMappingState: VarargMappingState,
|
||||
builtins: KotlinBuiltIns
|
||||
): Pair<KotlinType?, VarargMappingState> {
|
||||
val elementType = substitutedParameter.varargElementType
|
||||
?: error("Vararg parameter $substitutedParameter does not have vararg type")
|
||||
|
||||
return when (varargMappingState) {
|
||||
VarargMappingState.UNMAPPED -> {
|
||||
if (KotlinBuiltIns.isArrayOrPrimitiveArray(expectedParameterType) ||
|
||||
csBuilder.isTypeVariable(expectedParameterType)
|
||||
) {
|
||||
val arrayType = builtins.getPrimitiveArrayKotlinTypeByPrimitiveKotlinType(elementType)
|
||||
?: builtins.getArrayType(Variance.OUT_VARIANCE, elementType)
|
||||
arrayType to VarargMappingState.MAPPED_WITH_ARRAY
|
||||
} else {
|
||||
elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
}
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_PLAIN_ARGS -> {
|
||||
if (KotlinBuiltIns.isArrayOrPrimitiveArray(expectedParameterType))
|
||||
null to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
else
|
||||
elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_ARRAY ->
|
||||
null to VarargMappingState.MAPPED_WITH_ARRAY
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildReflectionType(
|
||||
descriptor: CallableDescriptor,
|
||||
dispatchReceiver: CallableReceiver?,
|
||||
extensionReceiver: CallableReceiver?,
|
||||
expectedType: UnwrappedType?,
|
||||
builtins: KotlinBuiltIns
|
||||
): Pair<UnwrappedType, CallableReferenceAdaptation?> {
|
||||
val argumentsAndReceivers = ArrayList<KotlinType>(descriptor.valueParameters.size + 2)
|
||||
|
||||
if (dispatchReceiver is CallableReceiver.UnboundReference) {
|
||||
argumentsAndReceivers.add(dispatchReceiver.receiver.stableType)
|
||||
}
|
||||
if (extensionReceiver is CallableReceiver.UnboundReference) {
|
||||
argumentsAndReceivers.add(extensionReceiver.receiver.stableType)
|
||||
}
|
||||
|
||||
val descriptorReturnType = descriptor.returnType
|
||||
?: ErrorUtils.createErrorType("Error return type for descriptor: $descriptor")
|
||||
|
||||
return when (descriptor) {
|
||||
is PropertyDescriptor -> {
|
||||
val mutable = descriptor.isVar && run {
|
||||
val setter = descriptor.setter
|
||||
setter == null || DescriptorVisibilities.isVisible(
|
||||
dispatchReceiver?.asReceiverValueForVisibilityChecks, setter,
|
||||
scopeTower.lexicalScope.ownerDescriptor
|
||||
)
|
||||
}
|
||||
|
||||
callComponents.reflectionTypes.getKPropertyType(
|
||||
Annotations.EMPTY,
|
||||
argumentsAndReceivers,
|
||||
descriptorReturnType,
|
||||
mutable
|
||||
) to null
|
||||
}
|
||||
is FunctionDescriptor -> {
|
||||
val callableReferenceAdaptation = getCallableReferenceAdaptation(
|
||||
descriptor, expectedType,
|
||||
unboundReceiverCount = argumentsAndReceivers.size,
|
||||
builtins = builtins
|
||||
)
|
||||
|
||||
val returnType = if (callableReferenceAdaptation == null) {
|
||||
descriptor.valueParameters.mapTo(argumentsAndReceivers) { it.type }
|
||||
descriptorReturnType
|
||||
} else {
|
||||
val arguments = callableReferenceAdaptation.argumentTypes
|
||||
val coercion = callableReferenceAdaptation.coercionStrategy
|
||||
argumentsAndReceivers.addAll(arguments)
|
||||
|
||||
if (coercion == CoercionStrategy.COERCION_TO_UNIT)
|
||||
descriptor.builtIns.unitType
|
||||
else
|
||||
descriptorReturnType
|
||||
}
|
||||
|
||||
val suspendConversionStrategy = callableReferenceAdaptation?.suspendConversionStrategy
|
||||
val isSuspend = descriptor.isSuspend || suspendConversionStrategy == SuspendConversionStrategy.SUSPEND_CONVERSION
|
||||
|
||||
callComponents.reflectionTypes.getKFunctionType(
|
||||
Annotations.EMPTY, null, argumentsAndReceivers, null,
|
||||
returnType, descriptor.builtIns, isSuspend
|
||||
) to callableReferenceAdaptation
|
||||
}
|
||||
else -> {
|
||||
assert(!descriptor.isSupportedForCallableReference()) { "${descriptor::class} isn't supported to use in callable references actually, but it's listed in `isSupportedForCallableReference` method" }
|
||||
ErrorUtils.createErrorType("Unsupported descriptor type: $descriptor") to null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toCallableReceiver(receiver: ReceiverValueWithSmartCastInfo, isExplicit: Boolean): CallableReceiver {
|
||||
if (!isExplicit) return CallableReceiver.ScopeReceiver(receiver)
|
||||
|
||||
return when (val lhsResult = argument.lhsResult) {
|
||||
is LHSResult.Expression -> CallableReceiver.ExplicitValueReceiver(receiver)
|
||||
is LHSResult.Type -> {
|
||||
if (lhsResult.qualifier?.classValueReceiver?.type == receiver.receiverValue.type) {
|
||||
CallableReceiver.BoundValueReference(receiver)
|
||||
} else {
|
||||
CallableReceiver.UnboundReference(receiver)
|
||||
}
|
||||
}
|
||||
is LHSResult.Object -> CallableReceiver.BoundValueReference(receiver)
|
||||
else -> throw IllegalStateException("Unsupported kind of lhsResult: $lhsResult")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class InputOutputTypes(val inputTypes: List<UnwrappedType>, val outputType: UnwrappedType)
|
||||
|
||||
fun extractInputOutputTypesFromCallableReferenceExpectedType(expectedType: UnwrappedType?): InputOutputTypes? {
|
||||
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.components
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.builtins.isFunctionType
|
||||
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemOperation
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.DetailedReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.CoercionStrategy
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class CallableReferencesCandidateFactory(
|
||||
val argument: CallableReferenceKotlinCallArgument,
|
||||
val callComponents: KotlinCallComponents,
|
||||
val scopeTower: ImplicitScopeTower,
|
||||
val compatibilityChecker: ((ConstraintSystemOperation) -> Unit) -> Unit,
|
||||
val expectedType: UnwrappedType?,
|
||||
private val csBuilder: ConstraintSystemOperation,
|
||||
private val resolutionCallbacks: KotlinResolutionCallbacks
|
||||
) : CandidateFactory<CallableReferenceCandidate> {
|
||||
|
||||
fun createCallableProcessor(explicitReceiver: DetailedReceiver?) =
|
||||
createCallableReferenceProcessor(scopeTower, argument.rhsName, this, explicitReceiver)
|
||||
|
||||
override fun createCandidate(
|
||||
towerCandidate: CandidateWithBoundDispatchReceiver,
|
||||
explicitReceiverKind: ExplicitReceiverKind,
|
||||
extensionReceiver: ReceiverValueWithSmartCastInfo?
|
||||
): CallableReferenceCandidate {
|
||||
|
||||
val dispatchCallableReceiver =
|
||||
towerCandidate.dispatchReceiver?.let { toCallableReceiver(it, explicitReceiverKind == ExplicitReceiverKind.DISPATCH_RECEIVER) }
|
||||
val extensionCallableReceiver = extensionReceiver?.let { toCallableReceiver(it, explicitReceiverKind == ExplicitReceiverKind.EXTENSION_RECEIVER) }
|
||||
val candidateDescriptor = towerCandidate.descriptor
|
||||
val diagnostics = SmartList<KotlinCallDiagnostic>()
|
||||
|
||||
val (reflectionCandidateType, callableReferenceAdaptation) = buildReflectionType(
|
||||
candidateDescriptor,
|
||||
dispatchCallableReceiver,
|
||||
extensionCallableReceiver,
|
||||
expectedType,
|
||||
callComponents.builtIns
|
||||
)
|
||||
|
||||
fun createReferenceCandidate(): CallableReferenceCandidate =
|
||||
CallableReferenceCandidate(
|
||||
candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
|
||||
explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation, diagnostics
|
||||
)
|
||||
|
||||
if (callComponents.statelessCallbacks.isHiddenInResolution(candidateDescriptor, argument, resolutionCallbacks)) {
|
||||
diagnostics.add(HiddenDescriptor)
|
||||
return createReferenceCandidate()
|
||||
}
|
||||
|
||||
if (needCompatibilityResolveForCallableReference(callableReferenceAdaptation, candidateDescriptor)) {
|
||||
markCandidateForCompatibilityResolve(diagnostics)
|
||||
}
|
||||
|
||||
if (callableReferenceAdaptation != null && expectedType != null && hasNonTrivialAdaptation(callableReferenceAdaptation)) {
|
||||
if (!expectedType.isFunctionType && !expectedType.isSuspendFunctionType) { // expectedType has some reflection type
|
||||
diagnostics.add(AdaptedCallableReferenceIsUsedWithReflection(argument))
|
||||
}
|
||||
}
|
||||
|
||||
if (callableReferenceAdaptation != null &&
|
||||
callableReferenceAdaptation.defaults != 0 &&
|
||||
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType)
|
||||
) {
|
||||
diagnostics.add(CallableReferencesDefaultArgumentUsed(argument, candidateDescriptor, callableReferenceAdaptation.defaults))
|
||||
}
|
||||
|
||||
if (candidateDescriptor !is CallableMemberDescriptor) {
|
||||
return CallableReferenceCandidate(
|
||||
candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
|
||||
explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation,
|
||||
listOf(NotCallableMemberReference(argument, candidateDescriptor))
|
||||
)
|
||||
}
|
||||
|
||||
diagnostics.addAll(towerCandidate.diagnostics)
|
||||
// todo smartcast on receiver diagnostic and CheckInstantiationOfAbstractClass
|
||||
|
||||
compatibilityChecker {
|
||||
if (it.hasContradiction) return@compatibilityChecker
|
||||
|
||||
val (_, visibilityError) = it.checkCallableReference(
|
||||
argument, dispatchCallableReceiver, extensionCallableReceiver, candidateDescriptor,
|
||||
reflectionCandidateType, expectedType, scopeTower.lexicalScope.ownerDescriptor
|
||||
)
|
||||
|
||||
diagnostics.addIfNotNull(visibilityError)
|
||||
|
||||
if (it.hasContradiction) diagnostics.add(
|
||||
CallableReferenceNotCompatible(
|
||||
argument,
|
||||
candidateDescriptor,
|
||||
expectedType,
|
||||
reflectionCandidateType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return createReferenceCandidate()
|
||||
}
|
||||
|
||||
private fun needCompatibilityResolveForCallableReference(
|
||||
callableReferenceAdaptation: CallableReferenceAdaptation?,
|
||||
candidate: CallableDescriptor
|
||||
): Boolean {
|
||||
// KT-13934: reference to companion object member via class name
|
||||
if (candidate.containingDeclaration.isCompanionObject() && argument.lhsResult is LHSResult.Type) return true
|
||||
|
||||
if (callableReferenceAdaptation == null) return false
|
||||
|
||||
return hasNonTrivialAdaptation(callableReferenceAdaptation)
|
||||
}
|
||||
|
||||
private fun hasNonTrivialAdaptation(callableReferenceAdaptation: CallableReferenceAdaptation) =
|
||||
callableReferenceAdaptation.defaults != 0 ||
|
||||
callableReferenceAdaptation.suspendConversionStrategy != SuspendConversionStrategy.NO_CONVERSION ||
|
||||
callableReferenceAdaptation.coercionStrategy != CoercionStrategy.NO_COERCION ||
|
||||
callableReferenceAdaptation.mappedArguments.values.any { it is ResolvedCallArgument.VarargArgument }
|
||||
|
||||
private enum class VarargMappingState {
|
||||
UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY
|
||||
}
|
||||
|
||||
private fun getCallableReferenceAdaptation(
|
||||
descriptor: FunctionDescriptor,
|
||||
expectedType: UnwrappedType?,
|
||||
unboundReceiverCount: Int,
|
||||
builtins: KotlinBuiltIns
|
||||
): CallableReferenceAdaptation? {
|
||||
if (callComponents.languageVersionSettings.apiVersion < ApiVersion.KOTLIN_1_4) return null
|
||||
|
||||
if (expectedType == null) return null
|
||||
|
||||
// Do not adapt references against KCallable type as it's impossible to map defaults/vararg to absent parameters of KCallable
|
||||
if (ReflectionTypes.hasKCallableTypeFqName(expectedType)) return null
|
||||
|
||||
val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null
|
||||
|
||||
val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount
|
||||
if (expectedArgumentCount < 0) return null
|
||||
|
||||
val fakeArguments = createFakeArgumentsForReference(descriptor, expectedArgumentCount, inputOutputTypes, unboundReceiverCount)
|
||||
val argumentMapping =
|
||||
callComponents.argumentsToParametersMapper.mapArguments(fakeArguments, externalArgument = null, descriptor = descriptor)
|
||||
if (argumentMapping.diagnostics.any { !it.candidateApplicability.isSuccess }) return null
|
||||
|
||||
/**
|
||||
* (A, B, C) -> Unit
|
||||
* fun foo(a: A, b: B = B(), vararg c: C)
|
||||
*/
|
||||
var defaults = 0
|
||||
var varargMappingState = VarargMappingState.UNMAPPED
|
||||
val mappedArguments = linkedMapOf<ValueParameterDescriptor, ResolvedCallArgument>()
|
||||
val mappedVarargElements = linkedMapOf<ValueParameterDescriptor, MutableList<KotlinCallArgument>>()
|
||||
val mappedArgumentTypes = arrayOfNulls<KotlinType?>(fakeArguments.size)
|
||||
|
||||
for ((valueParameter, resolvedArgument) in argumentMapping.parameterToCallArgumentMap) {
|
||||
for (fakeArgument in resolvedArgument.arguments) {
|
||||
val index = (fakeArgument as FakeKotlinCallArgumentForCallableReference).index
|
||||
val substitutedParameter = descriptor.valueParameters.getOrNull(valueParameter.index) ?: continue
|
||||
|
||||
val mappedArgument: KotlinType?
|
||||
if (substitutedParameter.isVararg) {
|
||||
val (varargType, newVarargMappingState) = varargParameterTypeByExpectedParameter(
|
||||
inputOutputTypes.inputTypes[index + unboundReceiverCount],
|
||||
substitutedParameter,
|
||||
varargMappingState,
|
||||
builtins
|
||||
)
|
||||
varargMappingState = newVarargMappingState
|
||||
mappedArgument = varargType
|
||||
|
||||
when (newVarargMappingState) {
|
||||
VarargMappingState.MAPPED_WITH_ARRAY -> {
|
||||
// If we've already mapped an argument to this value parameter, it'll always be a type mismatch.
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.SimpleArgument(fakeArgument)
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_PLAIN_ARGS -> {
|
||||
mappedVarargElements.getOrPut(valueParameter) { ArrayList() }.add(fakeArgument)
|
||||
}
|
||||
VarargMappingState.UNMAPPED -> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mappedArgument = substitutedParameter.type
|
||||
mappedArguments[valueParameter] = resolvedArgument
|
||||
}
|
||||
|
||||
mappedArgumentTypes[index] = mappedArgument
|
||||
}
|
||||
if (resolvedArgument == ResolvedCallArgument.DefaultArgument) {
|
||||
defaults++
|
||||
mappedArguments[valueParameter] = resolvedArgument
|
||||
}
|
||||
}
|
||||
if (mappedArgumentTypes.any { it == null }) return null
|
||||
|
||||
for ((valueParameter, varargElements) in mappedVarargElements) {
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.VarargArgument(varargElements)
|
||||
}
|
||||
|
||||
for (valueParameter in descriptor.valueParameters) {
|
||||
if (valueParameter.isVararg && valueParameter !in mappedArguments) {
|
||||
mappedArguments[valueParameter] = ResolvedCallArgument.VarargArgument(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
// lower(Unit!) = Unit
|
||||
val returnExpectedType = inputOutputTypes.outputType
|
||||
|
||||
val coercion =
|
||||
if (returnExpectedType.isUnit() && descriptor.returnType?.isUnit() == false)
|
||||
CoercionStrategy.COERCION_TO_UNIT
|
||||
else
|
||||
CoercionStrategy.NO_COERCION
|
||||
|
||||
val adaptedArguments =
|
||||
if (ReflectionTypes.isBaseTypeForNumberedReferenceTypes(expectedType))
|
||||
emptyMap()
|
||||
else
|
||||
mappedArguments
|
||||
|
||||
val suspendConversionStrategy =
|
||||
if (!descriptor.isSuspend && expectedType.isSuspendFunctionType) {
|
||||
SuspendConversionStrategy.SUSPEND_CONVERSION
|
||||
} else {
|
||||
SuspendConversionStrategy.NO_CONVERSION
|
||||
}
|
||||
|
||||
return CallableReferenceAdaptation(
|
||||
@Suppress("UNCHECKED_CAST") (mappedArgumentTypes as Array<KotlinType>),
|
||||
coercion, defaults,
|
||||
adaptedArguments,
|
||||
suspendConversionStrategy
|
||||
)
|
||||
}
|
||||
|
||||
private fun createFakeArgumentsForReference(
|
||||
descriptor: FunctionDescriptor,
|
||||
expectedArgumentCount: Int,
|
||||
inputOutputTypes: InputOutputTypes,
|
||||
unboundReceiverCount: Int
|
||||
): List<FakeKotlinCallArgumentForCallableReference> {
|
||||
var afterVararg = false
|
||||
var varargComponentType: UnwrappedType? = null
|
||||
var vararg = false
|
||||
return (0 until expectedArgumentCount).map { index ->
|
||||
val inputType = inputOutputTypes.inputTypes.getOrNull(index + unboundReceiverCount)
|
||||
if (vararg && varargComponentType != inputType) {
|
||||
afterVararg = true
|
||||
}
|
||||
|
||||
val valueParameter = descriptor.valueParameters.getOrNull(index)
|
||||
val name =
|
||||
if (afterVararg && valueParameter?.declaresDefaultValue() == true)
|
||||
valueParameter.name
|
||||
else
|
||||
null
|
||||
|
||||
if (valueParameter?.isVararg == true) {
|
||||
varargComponentType = inputType
|
||||
vararg = true
|
||||
}
|
||||
FakeKotlinCallArgumentForCallableReference(index, name)
|
||||
}
|
||||
}
|
||||
|
||||
private fun varargParameterTypeByExpectedParameter(
|
||||
expectedParameterType: KotlinType,
|
||||
substitutedParameter: ValueParameterDescriptor,
|
||||
varargMappingState: VarargMappingState,
|
||||
builtins: KotlinBuiltIns
|
||||
): Pair<KotlinType?, VarargMappingState> {
|
||||
val elementType = substitutedParameter.varargElementType
|
||||
?: error("Vararg parameter $substitutedParameter does not have vararg type")
|
||||
|
||||
return when (varargMappingState) {
|
||||
VarargMappingState.UNMAPPED -> {
|
||||
if (KotlinBuiltIns.isArrayOrPrimitiveArray(expectedParameterType) ||
|
||||
csBuilder.isTypeVariable(expectedParameterType)
|
||||
) {
|
||||
val arrayType = builtins.getPrimitiveArrayKotlinTypeByPrimitiveKotlinType(elementType)
|
||||
?: builtins.getArrayType(Variance.OUT_VARIANCE, elementType)
|
||||
arrayType to VarargMappingState.MAPPED_WITH_ARRAY
|
||||
} else {
|
||||
elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
}
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_PLAIN_ARGS -> {
|
||||
if (KotlinBuiltIns.isArrayOrPrimitiveArray(expectedParameterType))
|
||||
null to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
else
|
||||
elementType to VarargMappingState.MAPPED_WITH_PLAIN_ARGS
|
||||
}
|
||||
VarargMappingState.MAPPED_WITH_ARRAY ->
|
||||
null to VarargMappingState.MAPPED_WITH_ARRAY
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildReflectionType(
|
||||
descriptor: CallableDescriptor,
|
||||
dispatchReceiver: CallableReceiver?,
|
||||
extensionReceiver: CallableReceiver?,
|
||||
expectedType: UnwrappedType?,
|
||||
builtins: KotlinBuiltIns
|
||||
): Pair<UnwrappedType, CallableReferenceAdaptation?> {
|
||||
val argumentsAndReceivers = ArrayList<KotlinType>(descriptor.valueParameters.size + 2)
|
||||
|
||||
if (dispatchReceiver is CallableReceiver.UnboundReference) {
|
||||
argumentsAndReceivers.add(dispatchReceiver.receiver.stableType)
|
||||
}
|
||||
if (extensionReceiver is CallableReceiver.UnboundReference) {
|
||||
argumentsAndReceivers.add(extensionReceiver.receiver.stableType)
|
||||
}
|
||||
|
||||
val descriptorReturnType = descriptor.returnType
|
||||
?: ErrorUtils.createErrorType("Error return type for descriptor: $descriptor")
|
||||
|
||||
return when (descriptor) {
|
||||
is PropertyDescriptor -> {
|
||||
val mutable = descriptor.isVar && run {
|
||||
val setter = descriptor.setter
|
||||
setter == null || DescriptorVisibilities.isVisible(
|
||||
dispatchReceiver?.asReceiverValueForVisibilityChecks, setter,
|
||||
scopeTower.lexicalScope.ownerDescriptor
|
||||
)
|
||||
}
|
||||
|
||||
callComponents.reflectionTypes.getKPropertyType(
|
||||
Annotations.EMPTY,
|
||||
argumentsAndReceivers,
|
||||
descriptorReturnType,
|
||||
mutable
|
||||
) to null
|
||||
}
|
||||
is FunctionDescriptor -> {
|
||||
val callableReferenceAdaptation = getCallableReferenceAdaptation(
|
||||
descriptor, expectedType,
|
||||
unboundReceiverCount = argumentsAndReceivers.size,
|
||||
builtins = builtins
|
||||
)
|
||||
|
||||
val returnType = if (callableReferenceAdaptation == null) {
|
||||
descriptor.valueParameters.mapTo(argumentsAndReceivers) { it.type }
|
||||
descriptorReturnType
|
||||
} else {
|
||||
val arguments = callableReferenceAdaptation.argumentTypes
|
||||
val coercion = callableReferenceAdaptation.coercionStrategy
|
||||
argumentsAndReceivers.addAll(arguments)
|
||||
|
||||
if (coercion == CoercionStrategy.COERCION_TO_UNIT)
|
||||
descriptor.builtIns.unitType
|
||||
else
|
||||
descriptorReturnType
|
||||
}
|
||||
|
||||
val suspendConversionStrategy = callableReferenceAdaptation?.suspendConversionStrategy
|
||||
val isSuspend = descriptor.isSuspend || suspendConversionStrategy == SuspendConversionStrategy.SUSPEND_CONVERSION
|
||||
|
||||
callComponents.reflectionTypes.getKFunctionType(
|
||||
Annotations.EMPTY, null, argumentsAndReceivers, null,
|
||||
returnType, descriptor.builtIns, isSuspend
|
||||
) to callableReferenceAdaptation
|
||||
}
|
||||
else -> {
|
||||
assert(!descriptor.isSupportedForCallableReference()) { "${descriptor::class} isn't supported to use in callable references actually, but it's listed in `isSupportedForCallableReference` method" }
|
||||
ErrorUtils.createErrorType("Unsupported descriptor type: $descriptor") to null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toCallableReceiver(receiver: ReceiverValueWithSmartCastInfo, isExplicit: Boolean): CallableReceiver {
|
||||
if (!isExplicit) return CallableReceiver.ScopeReceiver(receiver)
|
||||
|
||||
return when (val lhsResult = argument.lhsResult) {
|
||||
is LHSResult.Expression -> CallableReceiver.ExplicitValueReceiver(receiver)
|
||||
is LHSResult.Type -> {
|
||||
if (lhsResult.qualifier?.classValueReceiver?.type == receiver.receiverValue.type) {
|
||||
CallableReceiver.BoundValueReference(receiver)
|
||||
} else {
|
||||
CallableReceiver.UnboundReference(receiver)
|
||||
}
|
||||
}
|
||||
is LHSResult.Object -> CallableReceiver.BoundValueReference(receiver)
|
||||
else -> throw IllegalStateException("Unsupported kind of lhsResult: $lhsResult")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user