[NI] Convert extension lambda to the non-extension one, if needed

#KT-30245 fixed
This commit is contained in:
Ilya Chernikov
2019-12-04 12:25:28 +01:00
parent ef5fe0675a
commit caa677e6d2
8 changed files with 394 additions and 23 deletions
@@ -118,16 +118,51 @@ private fun extractLambdaInfoFromFunctionalType(
returnTypeVariable: TypeVariableForLambdaReturnType? = null
): ResolvedLambdaAtom? {
if (expectedType == null || !expectedType.isBuiltinFunctionalType) return null
val parameters = extractLambdaParameters(expectedType, argument)
val parametersTypes = argument.parametersTypes
val expectedParameters = expectedType.getValueParameterTypesFromFunctionType()
val expectedReceiver = expectedType.getReceiverTypeFromFunctionType()?.unwrap()
val argumentAsFunctionExpression = argument.safeAs<FunctionExpression>()
val receiverType = argumentAsFunctionExpression?.receiverType ?: expectedType.getReceiverTypeFromFunctionType()?.unwrap()
val receiverFromExpected = argumentAsFunctionExpression?.receiverType == null && expectedReceiver != null
fun UnwrappedType?.orExpected(index: Int) =
this ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: expectedType.builtIns.nullableAnyType
// Extracting parameters and receiver type, taking into account the actual lambda definition and expected lambda type
val (parameters, receiver) = when {
argumentAsFunctionExpression != null ->
// lambda has explicit functional type - use types from it if available
(parametersTypes?.mapIndexed { index, type ->
type.orExpected(index)
} ?: emptyList()) to argumentAsFunctionExpression.receiverType
(parametersTypes?.size ?: 0) == expectedParameters.size && receiverFromExpected ->
// expected type has receiver, but arguments sizes are the same in actual and expected, so assuming missing (maybe unused) receiver in lambda
// TODO: in case of implicit parameters in lambda ("this" and "it") this case assumes "this", probably we should generate two possible overloads and choose among them later
(parametersTypes?.mapIndexed { index, type ->
type.orExpected(index)
} ?: expectedParameters.map { it.type.unwrap() }) to expectedReceiver
(parametersTypes?.size ?: 0) - expectedParameters.size == 1 && receiverFromExpected -> {
// one "missing" parameter in the expected parameters - first lambda parameter should be mapped to expected receiver
// TODO: same "this" or "it" case from above could be applicable here as well
(parametersTypes?.mapIndexed { index, type ->
type ?: run {
if (index == 0) expectedReceiver?.unwrap()
else expectedParameters.getOrNull(index - 1)?.type?.unwrap()
} ?: expectedType.builtIns.nullableAnyType
} ?: expectedParameters.map { it.type.unwrap() }) to null
}
else ->
(parametersTypes?.mapIndexed { index, type ->
type.orExpected(index)
} ?: expectedParameters.map { it.type.unwrap() }) to (if (receiverFromExpected) expectedReceiver else null)
}
val returnType = argumentAsFunctionExpression?.returnType ?: expectedType.getReturnTypeFromFunctionType().unwrap()
return ResolvedLambdaAtom(
argument,
expectedType.isSuspendFunctionType,
receiverType,
receiver,
parameters,
returnType,
typeVariableForLambdaReturnType = returnTypeVariable,
@@ -135,18 +170,6 @@ private fun extractLambdaInfoFromFunctionalType(
)
}
private fun extractLambdaParameters(expectedType: UnwrappedType, argument: LambdaKotlinCallArgument): List<UnwrappedType> {
val parametersTypes = argument.parametersTypes
val expectedParameters = expectedType.getValueParameterTypesFromFunctionType()
if (parametersTypes == null) {
return expectedParameters.map { it.type.unwrap() }
}
return parametersTypes.mapIndexed { index, type ->
type ?: expectedParameters.getOrNull(index)?.type?.unwrap() ?: expectedType.builtIns.nullableAnyType
}
}
fun LambdaWithTypeVariableAsExpectedTypeAtom.transformToResolvedLambda(
csBuilder: ConstraintSystemBuilder,
expectedType: UnwrappedType? = null,
@@ -159,7 +182,7 @@ fun LambdaWithTypeVariableAsExpectedTypeAtom.transformToResolvedLambda(
atom,
fixedExpectedType,
forceResolution = true,
returnTypeVariable
returnTypeVariable = returnTypeVariable
) as ResolvedLambdaAtom
setAnalyzed(resolvedLambdaAtom)
@@ -5,10 +5,12 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.annotations.FilteredAnnotations
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.addSubsystemFromArgument
import org.jetbrains.kotlin.resolve.calls.inference.model.*
@@ -72,6 +74,8 @@ class PostponedArgumentsAnalyzer(
fun substitute(type: UnwrappedType) = currentSubstitutor.safeSubstitute(c, type) as UnwrappedType
// Expected type has a higher priority against which lambda should be analyzed
// Mostly, this is needed to report more specific diagnostics on lambda parameters
fun expectedOrActualType(expected: UnwrappedType?, actual: UnwrappedType?): UnwrappedType? {
val expectedSubstituted = expected?.let(::substitute)
return if (expectedSubstituted != null && c.canBeProper(expectedSubstituted)) expectedSubstituted else actual?.let(::substitute)
@@ -79,14 +83,22 @@ class PostponedArgumentsAnalyzer(
val builtIns = c.getBuilder().builtIns
// Expected type has a higher priority against which lambda should be analyzed
// Mostly, this is needed to report more specific diagnostics on lambda parameters
val receiver = expectedOrActualType(lambda.expectedType.receiver(), lambda.receiver)
val expectedParameters = lambda.expectedType.valueParameters()
val expectedReceiver = lambda.expectedType.receiver()
val receiver = lambda.receiver?.let {
expectedOrActualType(expectedReceiver ?: expectedParameters?.getOrNull(0), lambda.receiver)
}
val expectedParametersToMatchAgainst = when {
receiver == null && expectedReceiver != null && expectedParameters != null -> listOf(expectedReceiver) + expectedParameters
receiver == null && expectedReceiver != null -> listOf(expectedReceiver)
receiver != null && expectedReceiver == null -> expectedParameters?.drop(1)
else -> expectedParameters
}
val parameters =
expectedParameters?.mapIndexed { index, expected ->
expectedParametersToMatchAgainst?.mapIndexed { index, expected ->
expectedOrActualType(expected, lambda.parameters.getOrNull(index)) ?: builtIns.nothingType
} ?: lambda.parameters.map(::substitute)
@@ -101,13 +113,18 @@ class PostponedArgumentsAnalyzer(
else -> null
}
val convertedAnnotations = lambda.expectedType?.annotations?.let { annotations ->
if (receiver != null || expectedReceiver == null) annotations
else FilteredAnnotations(annotations, true) { it != KotlinBuiltIns.FQ_NAMES.extensionFunctionType }
}
val (returnArguments, inferenceSession) = resolutionCallbacks.analyzeAndGetLambdaReturnArguments(
lambda.atom,
lambda.isSuspend,
receiver,
parameters,
expectedTypeForReturnArguments,
lambda.expectedType?.annotations ?: Annotations.EMPTY,
convertedAnnotations ?: Annotations.EMPTY,
stubsForPostponedVariables.cast()
)