[NI] Fix input/output types for callable reference atom

Input and output types are crucial for type variable fixation order and
 analysis of postponed arguments (callable references, lambdas).

 Specifically, if there is non-fixed type variable inside input types of
 a callable reference, then we'll postpone resolution for such callable
 reference.

 Initial example with the expected type `KMutableProperty1<*, F>` caused
 problems because input types were computed incorrectly (while there
 aren't input types here)

 #KT-25431 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-12-05 12:44:09 +03:00
parent 6ebbb6eae3
commit e8a8318ead
10 changed files with 139 additions and 35 deletions
@@ -5,9 +5,7 @@
package org.jetbrains.kotlin.resolve.calls.components
import org.jetbrains.kotlin.builtins.ReflectionTypes
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.builtins.*
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.calls.components.CreateFreshVariablesSubstitutor.createToFreshVariableSubstitutorAndAddInitialConstraints
@@ -28,6 +26,7 @@ import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.expressions.CoercionStrategy
import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.supertypes
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -223,9 +222,9 @@ class CallableReferencesCandidateFactory(
expectedType: UnwrappedType?,
unboundReceiverCount: Int
): Triple<Array<KotlinType>, CoercionStrategy, Int>? {
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null
val inputOutputTypes = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType) ?: return null
val expectedArgumentCount = functionType.arguments.size - unboundReceiverCount - 1 // 1 -- return type
val expectedArgumentCount = inputOutputTypes.inputTypes.size - unboundReceiverCount
if (expectedArgumentCount < 0) return null
val fakeArguments = (0..(expectedArgumentCount - 1)).map { FakeKotlinCallArgumentForCallableReference(it) }
@@ -251,7 +250,7 @@ class CallableReferencesCandidateFactory(
if (mappedArguments.any { it == null }) return null
// lower(Unit!) = Unit
val returnExpectedType = functionType.getReturnTypeFromFunctionType().lowerIfFlexible()
val returnExpectedType = inputOutputTypes.outputType
val coercion = if (returnExpectedType.isUnit()) CoercionStrategy.COERCION_TO_UNIT else CoercionStrategy.NO_COERCION
@@ -342,14 +341,43 @@ class CallableReferencesCandidateFactory(
}
}
fun getFunctionTypeFromCallableReferenceExpectedType(expectedType: UnwrappedType?): UnwrappedType? {
data class InputOutputTypes(val inputTypes: List<UnwrappedType>, val outputType: UnwrappedType)
fun extractInputOutputTypesFromCallableReferenceExpectedType(expectedType: UnwrappedType?): InputOutputTypes? {
if (expectedType == null) return null
return if (expectedType.isFunctionType) {
expectedType
} else if (ReflectionTypes.isNumberedKFunctionOrKSuspendFunction(expectedType)) {
expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap()
} else {
null
return when {
expectedType.isFunctionType ->
extractInputOutputTypesFromFunctionType(expectedType)
expectedType.isBaseTypeForNumberedReferenceTypes() ->
InputOutputTypes(emptyList(), expectedType.arguments.single().type.unwrap())
ReflectionTypes.isNumberedKFunctionOrKSuspendFunction(expectedType) -> {
val functionFromSupertype = expectedType.immediateSupertypes().first { it.isFunctionType }.unwrap()
extractInputOutputTypesFromFunctionType(functionFromSupertype)
}
ReflectionTypes.isNumberedKPropertyOrKMutablePropertyType(expectedType) -> {
val functionFromSupertype = expectedType.supertypes().first { it.isFunctionType }.unwrap()
extractInputOutputTypesFromFunctionType(functionFromSupertype)
}
else -> null
}
}
}
private fun UnwrappedType.isBaseTypeForNumberedReferenceTypes(): Boolean =
ReflectionTypes.hasKPropertyTypeFqName(this) ||
ReflectionTypes.hasKMutablePropertyTypeFqName(this) ||
ReflectionTypes.hasKCallableTypeFqName(this)
private fun extractInputOutputTypesFromFunctionType(functionType: UnwrappedType): InputOutputTypes {
val receiver = functionType.getReceiverTypeFromFunctionType()?.unwrap()
val parameters = functionType.getValueParameterTypesFromFunctionType().map { it.type.unwrap() }
val inputTypes = listOfNotNull(receiver) + parameters
val outputType = functionType.getReturnTypeFromFunctionType().unwrap()
return InputOutputTypes(inputTypes, outputType)
}
@@ -5,14 +5,9 @@
package org.jetbrains.kotlin.resolve.calls.model
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.components.CallableReferenceCandidate
import org.jetbrains.kotlin.resolve.calls.components.TypeArgumentsToParametersMapper
import org.jetbrains.kotlin.resolve.calls.components.getFunctionTypeFromCallableReferenceExpectedType
import org.jetbrains.kotlin.resolve.calls.components.*
import org.jetbrains.kotlin.resolve.calls.inference.components.FreshVariableNewTypeSubstitutor
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableForLambdaReturnType
@@ -132,18 +127,10 @@ class ResolvedCallableReferenceAtom(
}
override val inputTypes: Collection<UnwrappedType>
get() {
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return listOfNotNull(expectedType)
val parameters = functionType.getValueParameterTypesFromFunctionType().map { it.type.unwrap() }
val receiver = functionType.getReceiverTypeFromFunctionType()?.unwrap()
return receiver?.let { parameters + it } ?: parameters
}
get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.inputTypes ?: listOfNotNull(expectedType)
override val outputType: UnwrappedType?
get() {
val functionType = getFunctionTypeFromCallableReferenceExpectedType(expectedType) ?: return null
return functionType.getReturnTypeFromFunctionType().unwrap()
}
get() = extractInputOutputTypesFromCallableReferenceExpectedType(expectedType)?.outputType
}
class ResolvedCollectionLiteralAtom(