FE: provide argument mapping info for adapted callable reference

This commit is contained in:
Dmitry Petrov
2020-01-15 18:41:03 +03:00
parent da7d594d0f
commit 89c832b5a0
4 changed files with 104 additions and 26 deletions
@@ -181,6 +181,18 @@ class FakeValueArgumentForLeftCallableReference(val ktExpression: KtCallableRefe
override fun isExternal(): Boolean = false override fun isExternal(): Boolean = false
} }
class FakePositionalValueArgumentForCallableReferenceImpl(
private val callElement: KtElement,
override val index: Int
) : FakePositionalValueArgumentForCallableReference {
override fun getArgumentExpression(): KtExpression? = null
override fun getArgumentName(): ValueArgumentName? = null
override fun isNamed(): Boolean = false
override fun asElement(): KtElement = callElement
override fun getSpreadElement(): LeafPsiElement? = null
override fun isExternal(): Boolean = false
}
class EmptyLabeledReturn( class EmptyLabeledReturn(
val returnExpression: KtReturnExpression, val returnExpression: KtReturnExpression,
builtIns: KotlinBuiltIns builtIns: KotlinBuiltIns
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.MissingSupertypesResolver import org.jetbrains.kotlin.resolve.MissingSupertypesResolver
@@ -20,6 +21,8 @@ import org.jetbrains.kotlin.resolve.TemporaryBindingTrace
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceAdaptation
import org.jetbrains.kotlin.resolve.calls.components.isVararg
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
@@ -216,7 +219,7 @@ class ResolvedAtomCompleter(
} }
val functionDescriptor = trace.bindingContext.get(BindingContext.FUNCTION, ktFunction) as? FunctionDescriptorImpl val functionDescriptor = trace.bindingContext.get(BindingContext.FUNCTION, ktFunction) as? FunctionDescriptorImpl
?: throw AssertionError("No function descriptor for resolved lambda argument") ?: throw AssertionError("No function descriptor for resolved lambda argument")
functionDescriptor.setReturnType(returnType) functionDescriptor.setReturnType(returnType)
val existingLambdaType = trace.getType(ktArgumentExpression) val existingLambdaType = trace.getType(ktArgumentExpression)
@@ -277,7 +280,10 @@ class ResolvedAtomCompleter(
val resultTypeParameters = val resultTypeParameters =
callableCandidate.freshSubstitutor!!.freshVariables.map { resultSubstitutor.safeSubstitute(it.defaultType) } callableCandidate.freshSubstitutor!!.freshVariables.map { resultSubstitutor.safeSubstitute(it.defaultType) }
val typeParametersSubstitutor = NewTypeSubstitutorByConstructorMap((callableCandidate.candidate.typeParameters.map { it.typeConstructor } zip resultTypeParameters).toMap()) val typeParametersSubstitutor =
NewTypeSubstitutorByConstructorMap(
(callableCandidate.candidate.typeParameters.map { it.typeConstructor } zip resultTypeParameters).toMap()
)
val firstSubstitution = typeParametersSubstitutor.toOldSubstitution() val firstSubstitution = typeParametersSubstitutor.toOldSubstitution()
val secondSubstitution = resultSubstitutor.toOldSubstitution() val secondSubstitution = resultSubstitutor.toOldSubstitution()
@@ -317,6 +323,8 @@ class ResolvedAtomCompleter(
) )
resolvedCall.setResultingSubstitutor(resultSubstitutor) resolvedCall.setResultingSubstitutor(resultSubstitutor)
recordArgumentAdaptationForCallableReference(resolvedCall, callableCandidate.callableReferenceAdaptation)
tracing.bindCall(topLevelTrace, psiCall) tracing.bindCall(topLevelTrace, psiCall)
tracing.bindReference(topLevelTrace, resolvedCall) tracing.bindReference(topLevelTrace, resolvedCall)
tracing.bindResolvedCall(topLevelTrace, resolvedCall) tracing.bindResolvedCall(topLevelTrace, resolvedCall)
@@ -352,6 +360,47 @@ class ResolvedAtomCompleter(
kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, topLevelCallCheckerContext) kotlinToResolvedCallTransformer.runCallCheckers(resolvedCall, topLevelCallCheckerContext)
} }
private fun recordArgumentAdaptationForCallableReference(
resolvedCall: ResolvedCallImpl<CallableDescriptor>,
callableReferenceAdaptation: CallableReferenceAdaptation?
) {
if (callableReferenceAdaptation == null) return
val callElement = resolvedCall.call.callElement
for ((valueParameter, resolvedCallArgument) in callableReferenceAdaptation.mappedArguments) {
resolvedCall.recordValueArgument(
valueParameter,
when (resolvedCallArgument) {
ResolvedCallArgument.DefaultArgument ->
DefaultValueArgument.DEFAULT
is ResolvedCallArgument.SimpleArgument -> {
val valueArgument = makeFakeValueArgument(resolvedCallArgument.callArgument, callElement)
if (valueParameter.isVararg)
VarargValueArgument(listOf(valueArgument))
else
ExpressionValueArgument(valueArgument)
}
is ResolvedCallArgument.VarargArgument ->
VarargValueArgument(
resolvedCallArgument.arguments.map {
makeFakeValueArgument(it, callElement)
}
)
}
)
}
}
private fun makeFakeValueArgument(
callArgument: KotlinCallArgument,
callElement: KtElement
): ValueArgument {
val fakeCallArgument = callArgument as? FakeKotlinCallArgumentForCallableReference
?: throw AssertionError("FakeKotlinCallArgumentForCallableReference expected: $callArgument")
return FakePositionalValueArgumentForCallableReferenceImpl(callElement, fakeCallArgument.index)
}
private fun completeCollectionLiteralCalls(collectionLiteralArgument: ResolvedCollectionLiteralAtom) { private fun completeCollectionLiteralCalls(collectionLiteralArgument: ResolvedCollectionLiteralAtom) {
val psiCallArgument = collectionLiteralArgument.atom.psiCallArgument as CollectionLiteralKotlinCallArgumentImpl val psiCallArgument = collectionLiteralArgument.atom.psiCallArgument as CollectionLiteralKotlinCallArgumentImpl
val context = psiCallArgument.outerCallContext val context = psiCallArgument.outerCallContext
@@ -36,6 +36,10 @@ interface ValueArgument {
fun isExternal(): Boolean fun isExternal(): Boolean
} }
interface FakePositionalValueArgumentForCallableReference : ValueArgument {
val index: Int
}
interface LambdaArgument : ValueArgument { interface LambdaArgument : ValueArgument {
fun getLambdaExpression(): KtLambdaExpression? fun getLambdaExpression(): KtLambdaExpression?
} }
@@ -59,7 +59,7 @@ class CallableReferenceCandidate(
val extensionReceiver: CallableReceiver?, val extensionReceiver: CallableReceiver?,
val explicitReceiverKind: ExplicitReceiverKind, val explicitReceiverKind: ExplicitReceiverKind,
val reflectionCandidateType: UnwrappedType, val reflectionCandidateType: UnwrappedType,
val numDefaults: Int, val callableReferenceAdaptation: CallableReferenceAdaptation?,
val diagnostics: List<KotlinCallDiagnostic> val diagnostics: List<KotlinCallDiagnostic>
) : Candidate { ) : Candidate {
override val resultingApplicability = getResultApplicability(diagnostics) override val resultingApplicability = getResultApplicability(diagnostics)
@@ -67,8 +67,17 @@ class CallableReferenceCandidate(
var freshSubstitutor: FreshVariableNewTypeSubstitutor? = null var freshSubstitutor: FreshVariableNewTypeSubstitutor? = null
internal set internal set
val numDefaults get() = callableReferenceAdaptation?.defaults ?: 0
} }
class CallableReferenceAdaptation(
val argumentTypes: Array<KotlinType>,
val coercionStrategy: CoercionStrategy,
val defaults: Int,
val mappedArguments: Map<ValueParameterDescriptor, ResolvedCallArgument>
)
/** /**
* cases: class A {}, class B { companion object }, object C, enum class D { E } * cases: class A {}, class B { companion object }, object C, enum class D { E }
* A::foo <-> Type * A::foo <-> Type
@@ -179,7 +188,7 @@ class CallableReferencesCandidateFactory(
val candidateDescriptor = towerCandidate.descriptor val candidateDescriptor = towerCandidate.descriptor
val diagnostics = SmartList<KotlinCallDiagnostic>() val diagnostics = SmartList<KotlinCallDiagnostic>()
val (reflectionCandidateType, defaults) = buildReflectionType( val (reflectionCandidateType, callableReferenceAdaptation) = buildReflectionType(
candidateDescriptor, candidateDescriptor,
dispatchCallableReceiver, dispatchCallableReceiver,
extensionCallableReceiver, extensionCallableReceiver,
@@ -187,16 +196,17 @@ class CallableReferencesCandidateFactory(
callComponents.builtIns callComponents.builtIns
) )
if (defaults != 0 && if (callableReferenceAdaptation != null &&
callableReferenceAdaptation.defaults != 0 &&
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType) !callComponents.languageVersionSettings.supportsFeature(LanguageFeature.FunctionReferenceWithDefaultValueAsOtherType)
) { ) {
diagnostics.add(CallableReferencesDefaultArgumentUsed(argument, candidateDescriptor, defaults)) diagnostics.add(CallableReferencesDefaultArgumentUsed(argument, candidateDescriptor, callableReferenceAdaptation.defaults))
} }
if (candidateDescriptor !is CallableMemberDescriptor) { if (candidateDescriptor !is CallableMemberDescriptor) {
return CallableReferenceCandidate( return CallableReferenceCandidate(
candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver, candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
explicitReceiverKind, reflectionCandidateType, defaults, explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation,
listOf(NotCallableMemberReference(argument, candidateDescriptor)) listOf(NotCallableMemberReference(argument, candidateDescriptor))
) )
} }
@@ -226,7 +236,7 @@ class CallableReferencesCandidateFactory(
return CallableReferenceCandidate( return CallableReferenceCandidate(
candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver, candidateDescriptor, dispatchCallableReceiver, extensionCallableReceiver,
explicitReceiverKind, reflectionCandidateType, defaults, diagnostics explicitReceiverKind, reflectionCandidateType, callableReferenceAdaptation, diagnostics
) )
} }
@@ -234,18 +244,18 @@ class CallableReferencesCandidateFactory(
UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY UNMAPPED, MAPPED_WITH_PLAIN_ARGS, MAPPED_WITH_ARRAY
} }
private fun getArgumentAndReturnTypeUseMappingByExpectedType( private fun getCallableReferenceAdaptation(
descriptor: FunctionDescriptor, descriptor: FunctionDescriptor,
expectedType: UnwrappedType?, expectedType: UnwrappedType?,
unboundReceiverCount: Int, unboundReceiverCount: Int,
builtins: KotlinBuiltIns builtins: KotlinBuiltIns
): Triple<Array<KotlinType>, CoercionStrategy, Int>? { ): CallableReferenceAdaptation? {
val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null
val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount
if (expectedArgumentCount < 0) return null if (expectedArgumentCount < 0) return null
val fakeArguments = (0..(expectedArgumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(it) } val fakeArguments = (0 until expectedArgumentCount).map { FakeKotlinCallArgumentForCallableReference(it) }
val argumentMapping = val argumentMapping =
callComponents.argumentsToParametersMapper.mapArguments(fakeArguments, externalArgument = null, descriptor = descriptor) callComponents.argumentsToParametersMapper.mapArguments(fakeArguments, externalArgument = null, descriptor = descriptor)
if (argumentMapping.diagnostics.any { !it.candidateApplicability.isSuccess }) return null if (argumentMapping.diagnostics.any { !it.candidateApplicability.isSuccess }) return null
@@ -288,8 +298,11 @@ class CallableReferencesCandidateFactory(
val coercion = if (returnExpectedType.isUnit()) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION val coercion = if (returnExpectedType.isUnit()) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION
@Suppress("UNCHECKED_CAST")
return Triple(mappedArguments as Array<KotlinType>, coercion, defaults) return CallableReferenceAdaptation(
@Suppress("UNCHECKED_CAST") (mappedArguments as Array<KotlinType>),
coercion, defaults, argumentMapping.parameterToCallArgumentMap
)
} }
private fun varargParameterTypeByExpectedParameter( private fun varargParameterTypeByExpectedParameter(
@@ -328,7 +341,7 @@ class CallableReferencesCandidateFactory(
extensionReceiver: CallableReceiver?, extensionReceiver: CallableReceiver?,
expectedType: UnwrappedType?, expectedType: UnwrappedType?,
builtins: KotlinBuiltIns builtins: KotlinBuiltIns
): Pair<UnwrappedType, /*defaults*/ Int> { ): Pair<UnwrappedType, CallableReferenceAdaptation?> {
val argumentsAndReceivers = ArrayList<KotlinType>(descriptor.valueParameters.size + 2) val argumentsAndReceivers = ArrayList<KotlinType>(descriptor.valueParameters.size + 2)
if (dispatchReceiver is CallableReceiver.UnboundReference) { if (dispatchReceiver is CallableReceiver.UnboundReference) {
@@ -356,35 +369,35 @@ class CallableReferencesCandidateFactory(
argumentsAndReceivers, argumentsAndReceivers,
descriptorReturnType, descriptorReturnType,
mutable mutable
) to 0 ) to null
} }
is FunctionDescriptor -> { is FunctionDescriptor -> {
val returnType: KotlinType val callableReferenceAdaptation = getCallableReferenceAdaptation(
val defaults: Int
val argumentsAndExpectedTypeCoercion = getArgumentAndReturnTypeUseMappingByExpectedType(
descriptor, expectedType, descriptor, expectedType,
unboundReceiverCount = argumentsAndReceivers.size, unboundReceiverCount = argumentsAndReceivers.size,
builtins = builtins builtins = builtins
) )
if (argumentsAndExpectedTypeCoercion == null) { val returnType = if (callableReferenceAdaptation == null) {
descriptor.valueParameters.mapTo(argumentsAndReceivers) { it.type } descriptor.valueParameters.mapTo(argumentsAndReceivers) { it.type }
returnType = descriptorReturnType descriptorReturnType
defaults = 0
} else { } else {
val (arguments, coercion) = argumentsAndExpectedTypeCoercion val arguments = callableReferenceAdaptation.argumentTypes
defaults = argumentsAndExpectedTypeCoercion.third val coercion = callableReferenceAdaptation.coercionStrategy
argumentsAndReceivers.addAll(arguments) argumentsAndReceivers.addAll(arguments)
returnType = if (coercion == CoercionStrategy.COERCION_TO_UNIT) descriptor.builtIns.unitType else descriptorReturnType if (coercion == CoercionStrategy.COERCION_TO_UNIT)
descriptor.builtIns.unitType
else
descriptorReturnType
} }
return callComponents.reflectionTypes.getKFunctionType( return callComponents.reflectionTypes.getKFunctionType(
Annotations.EMPTY, null, argumentsAndReceivers, null, Annotations.EMPTY, null, argumentsAndReceivers, null,
returnType, descriptor.builtIns, descriptor.isSuspend returnType, descriptor.builtIns, descriptor.isSuspend
) to defaults ) to callableReferenceAdaptation
} }
else -> return ErrorUtils.createErrorType("Unsupported descriptor type: $descriptor") to 0 else -> return ErrorUtils.createErrorType("Unsupported descriptor type: $descriptor") to null
} }
} }