[NI] Commonize type-conversions (SAM/Suspend)
- Allow participating subtypes of functional types in conversions - Fix several subtle inconsistencies - Place logic about conversions at one place Now conversions operations have two stages: before usual subtyping check and after one. This is needed to support conversions of subtypes (of functional types, for example). First, the compiler checks if it possible to resolve an argument without conversion and only then it tries to perform conversion. Note that it'd be incorrect to perform conversion eagerly as it can change resolve (Runnable & () -> Unit <: KRunnable), plus we can't guess whether conversion is needed at all as it's important not to look into supertypes if resolution doesn't actually needed it #KT-36448 Fixed #KT-37574 Fixed #KT-38604 Fixed
This commit is contained in:
+71
-21
@@ -413,7 +413,21 @@ private fun KotlinResolutionCandidate.resolveKotlinArgument(
|
||||
candidateParameter: ParameterDescriptor?,
|
||||
receiverInfo: ReceiverInfo
|
||||
) {
|
||||
val expectedType = candidateParameter?.let { prepareExpectedType(argument, candidateParameter) }
|
||||
val candidateExpectedType = candidateParameter?.let { argument.getExpectedType(it, callComponents.languageVersionSettings) }
|
||||
|
||||
val conversionDataBeforeSubtyping =
|
||||
if (candidateParameter == null || candidateExpectedType == null) {
|
||||
null
|
||||
} else {
|
||||
TypeConversions.performCompositeConversionBeforeSubtyping(
|
||||
this, argument, candidateParameter, candidateExpectedType
|
||||
)
|
||||
}
|
||||
|
||||
val convertedExpectedType = conversionDataBeforeSubtyping?.convertedType
|
||||
val unsubstitutedExpectedType = conversionDataBeforeSubtyping?.convertedType ?: candidateExpectedType
|
||||
val expectedType = unsubstitutedExpectedType?.let { prepareExpectedType(it) }
|
||||
|
||||
val convertedArgument = if (expectedType != null && shouldRunConversionForConstants(expectedType)) {
|
||||
val convertedConstant = resolutionCallbacks.convertSignedConstantToUnsigned(argument)
|
||||
if (convertedConstant != null) {
|
||||
@@ -423,8 +437,14 @@ private fun KotlinResolutionCandidate.resolveKotlinArgument(
|
||||
convertedConstant
|
||||
} else null
|
||||
|
||||
addResolvedKtPrimitive(
|
||||
resolveKtPrimitive(
|
||||
|
||||
if (candidateExpectedType == null || // Nothing to convert
|
||||
convertedExpectedType != null || // Type is already converted
|
||||
conversionDataBeforeSubtyping?.wasConversion == true || // We tried to convert type but failed
|
||||
conversionDataBeforeSubtyping?.conversionDefinitelyNotNeeded == true ||
|
||||
csBuilder.hasContradiction
|
||||
) {
|
||||
val resolvedAtom = resolveKtPrimitive(
|
||||
csBuilder,
|
||||
argument,
|
||||
expectedType,
|
||||
@@ -432,7 +452,54 @@ private fun KotlinResolutionCandidate.resolveKotlinArgument(
|
||||
receiverInfo,
|
||||
convertedArgument?.unknownIntegerType?.unwrap()
|
||||
)
|
||||
)
|
||||
|
||||
addResolvedKtPrimitive(resolvedAtom)
|
||||
} else {
|
||||
var convertedTypeAfterSubtyping: UnwrappedType? = null
|
||||
csBuilder.runTransaction {
|
||||
val resolvedAtom = resolveKtPrimitive(
|
||||
csBuilder,
|
||||
argument,
|
||||
expectedType,
|
||||
this@resolveKotlinArgument,
|
||||
receiverInfo,
|
||||
convertedArgument?.unknownIntegerType?.unwrap()
|
||||
)
|
||||
|
||||
if (!hasContradiction) {
|
||||
addResolvedKtPrimitive(resolvedAtom)
|
||||
return@runTransaction true
|
||||
}
|
||||
|
||||
convertedTypeAfterSubtyping =
|
||||
TypeConversions.performCompositeConversionAfterSubtyping(
|
||||
this@resolveKotlinArgument,
|
||||
argument,
|
||||
candidateParameter,
|
||||
candidateExpectedType
|
||||
)?.let { prepareExpectedType(it) }
|
||||
|
||||
if (convertedTypeAfterSubtyping == null) {
|
||||
addResolvedKtPrimitive(resolvedAtom)
|
||||
return@runTransaction true
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
if (convertedTypeAfterSubtyping != null) {
|
||||
val resolvedAtom = resolveKtPrimitive(
|
||||
csBuilder,
|
||||
argument,
|
||||
convertedTypeAfterSubtyping,
|
||||
this@resolveKotlinArgument,
|
||||
receiverInfo,
|
||||
convertedArgument?.unknownIntegerType?.unwrap()
|
||||
)
|
||||
addResolvedKtPrimitive(resolvedAtom)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinResolutionCandidate.shouldRunConversionForConstants(expectedType: UnwrappedType): Boolean {
|
||||
@@ -474,23 +541,6 @@ private fun KotlinResolutionCandidate.checkUnsafeImplicitInvokeAfterSafeCall(arg
|
||||
return ImplicitInvokeCheckStatus.INVOKE_ON_NOT_NULL_VARIABLE
|
||||
}
|
||||
|
||||
private fun KotlinResolutionCandidate.prepareExpectedType(
|
||||
argument: KotlinCallArgument,
|
||||
parameter: ParameterDescriptor
|
||||
): UnwrappedType {
|
||||
val expectedType =
|
||||
performExpectedTypeConversion(argument, parameter)
|
||||
?: argument.getExpectedType(parameter, callComponents.languageVersionSettings)
|
||||
return prepareExpectedType(expectedType)
|
||||
}
|
||||
|
||||
private fun KotlinResolutionCandidate.performExpectedTypeConversion(
|
||||
argument: KotlinCallArgument,
|
||||
parameter: ParameterDescriptor
|
||||
): UnwrappedType? {
|
||||
return getExpectedTypeWithSAMConversion(argument, parameter) ?: getExpectedTypeWithSuspendConversion(argument, parameter)
|
||||
}
|
||||
|
||||
private fun KotlinResolutionCandidate.prepareExpectedType(expectedType: UnwrappedType): UnwrappedType {
|
||||
val resultType = knownTypeParametersResultingSubstitutor?.substitute(expectedType) ?: expectedType
|
||||
return resolvedCall.freshVariablesSubstitutor.safeSubstitute(resultType)
|
||||
|
||||
+49
-53
@@ -17,23 +17,11 @@ import org.jetbrains.kotlin.resolve.sam.getFunctionTypeForPossibleSamType
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
|
||||
object SamTypeConversions {
|
||||
fun conversionMightBeNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean {
|
||||
return when (argument) {
|
||||
is SimpleKotlinCallArgument -> argument.receiver.stableType.isFunctionType
|
||||
is LambdaKotlinCallArgument, is CallableReferenceKotlinCallArgument -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun conversionMightBeNeededAfterSubtypingCheck(argument: KotlinCallArgument): Boolean {
|
||||
return argument is SimpleKotlinCallArgument && argument.receiver.stableType.isFunctionTypeOrSubtype
|
||||
}
|
||||
|
||||
|
||||
fun conversionDefinitelyNotNeeded(
|
||||
object SamTypeConversions : ParameterTypeConversion {
|
||||
override fun conversionDefinitelyNotNeeded(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
candidateParameter: ParameterDescriptor
|
||||
argument: KotlinCallArgument,
|
||||
expectedParameterType: UnwrappedType
|
||||
): Boolean {
|
||||
val callComponents = candidate.callComponents
|
||||
val generatingAdditionalSamCandidateIsEnabled =
|
||||
@@ -41,7 +29,7 @@ object SamTypeConversions {
|
||||
!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitVarargAsArrayAfterSamArgument)
|
||||
|
||||
if (generatingAdditionalSamCandidateIsEnabled) return true
|
||||
if (candidateParameter.type.isNothing()) return true
|
||||
if (expectedParameterType.isNothing()) return true
|
||||
|
||||
val samConversionOracle = callComponents.samConversionOracle
|
||||
if (!callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SamConversionForKotlinFunctions)) {
|
||||
@@ -50,48 +38,56 @@ object SamTypeConversions {
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinResolutionCandidate.getExpectedTypeWithSAMConversion(
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor
|
||||
): UnwrappedType? {
|
||||
if (SamTypeConversions.conversionDefinitelyNotNeeded(this, candidateParameter)) return null
|
||||
|
||||
val argumentIsFunctional = when (argument) {
|
||||
is SimpleKotlinCallArgument -> argument.receiver.stableType.isFunctionType
|
||||
is LambdaKotlinCallArgument, is CallableReferenceKotlinCallArgument -> true
|
||||
else -> false
|
||||
override fun conversionIsNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean {
|
||||
return when (argument) {
|
||||
is SimpleKotlinCallArgument -> argument.receiver.stableType.isFunctionType
|
||||
is LambdaKotlinCallArgument, is CallableReferenceKotlinCallArgument -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
if (!argumentIsFunctional) return null
|
||||
|
||||
val originalExpectedType = argument.getExpectedType(candidateParameter.original, callComponents.languageVersionSettings)
|
||||
override fun conversionIsNeededAfterSubtypingCheck(argument: KotlinCallArgument): Boolean {
|
||||
return argument is SimpleKotlinCallArgument && argument.receiver.stableType.isFunctionTypeOrSubtype
|
||||
}
|
||||
|
||||
val convertedTypeByOriginal =
|
||||
callComponents.samConversionResolver.getFunctionTypeForPossibleSamType(
|
||||
originalExpectedType,
|
||||
callComponents.samConversionOracle
|
||||
) ?: return null
|
||||
override fun convertParameterType(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
parameter: ParameterDescriptor,
|
||||
expectedParameterType: UnwrappedType
|
||||
): UnwrappedType? {
|
||||
val callComponents = candidate.callComponents
|
||||
val originalExpectedType = argument.getExpectedType(parameter.original, callComponents.languageVersionSettings)
|
||||
|
||||
val candidateExpectedType = argument.getExpectedType(candidateParameter, callComponents.languageVersionSettings)
|
||||
val convertedTypeByCandidate =
|
||||
callComponents.samConversionResolver.getFunctionTypeForPossibleSamType(
|
||||
candidateExpectedType,
|
||||
callComponents.samConversionOracle
|
||||
val convertedTypeByOriginal =
|
||||
callComponents.samConversionResolver.getFunctionTypeForPossibleSamType(
|
||||
originalExpectedType,
|
||||
callComponents.samConversionOracle
|
||||
) ?: return null
|
||||
|
||||
val convertedTypeByCandidate =
|
||||
callComponents.samConversionResolver.getFunctionTypeForPossibleSamType(
|
||||
expectedParameterType,
|
||||
callComponents.samConversionOracle
|
||||
)
|
||||
|
||||
assert(expectedParameterType.constructor == originalExpectedType.constructor && convertedTypeByCandidate != null) {
|
||||
"If original type is SAM type, then candidate should have same type constructor and corresponding function type\n" +
|
||||
"originalExpectType: $originalExpectedType, candidateExpectType: $expectedParameterType\n" +
|
||||
"functionTypeByOriginal: $convertedTypeByOriginal, functionTypeByCandidate: $convertedTypeByCandidate"
|
||||
}
|
||||
|
||||
candidate.resolvedCall.registerArgumentWithSamConversion(
|
||||
argument,
|
||||
SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!)
|
||||
)
|
||||
|
||||
assert(candidateExpectedType.constructor == originalExpectedType.constructor && convertedTypeByCandidate != null) {
|
||||
"If original type is SAM type, then candidate should have same type constructor and corresponding function type\n" +
|
||||
"originalExpectType: $originalExpectedType, candidateExpectType: $candidateExpectedType\n" +
|
||||
"functionTypeByOriginal: $convertedTypeByOriginal, functionTypeByCandidate: $convertedTypeByCandidate"
|
||||
val samDescriptor = originalExpectedType.constructor.declarationDescriptor
|
||||
if (samDescriptor is ClassDescriptor) {
|
||||
callComponents.lookupTracker.record(candidate.scopeTower.location, samDescriptor, SAM_LOOKUP_NAME)
|
||||
}
|
||||
|
||||
return convertedTypeByCandidate
|
||||
}
|
||||
|
||||
resolvedCall.registerArgumentWithSamConversion(argument, SamConversionDescription(convertedTypeByOriginal, convertedTypeByCandidate!!))
|
||||
|
||||
val samDescriptor = originalExpectedType.constructor.declarationDescriptor
|
||||
if (samDescriptor is ClassDescriptor) {
|
||||
callComponents.lookupTracker.record(scopeTower.location, samDescriptor, SAM_LOOKUP_NAME)
|
||||
}
|
||||
|
||||
return convertedTypeByCandidate
|
||||
}
|
||||
|
||||
+28
-29
@@ -11,11 +11,11 @@ import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
|
||||
object SuspendTypeConversions {
|
||||
fun conversionDefinitelyNotNeeded(
|
||||
object SuspendTypeConversions : ParameterTypeConversion {
|
||||
override fun conversionDefinitelyNotNeeded(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor
|
||||
expectedParameterType: UnwrappedType
|
||||
): Boolean {
|
||||
if (!candidate.callComponents.languageVersionSettings.supportsFeature(LanguageFeature.SuspendConversion)) return true
|
||||
|
||||
@@ -25,34 +25,33 @@ object SuspendTypeConversions {
|
||||
if (!argumentType.isFunctionType) return true
|
||||
if (argumentType.isSuspendFunctionType) return true
|
||||
|
||||
if (!candidateParameter.type.isSuspendFunctionType) return true
|
||||
if (!expectedParameterType.isSuspendFunctionType) return true
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
fun KotlinResolutionCandidate.conversionMightBeNeededBeforeSubtypingCheck(): Boolean = true
|
||||
fun KotlinResolutionCandidate.conversionMightBeNeededAfterSubtypingCheck(): Boolean = false
|
||||
override fun conversionIsNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean = true
|
||||
override fun conversionIsNeededAfterSubtypingCheck(argument: KotlinCallArgument): Boolean =
|
||||
argument is SimpleKotlinCallArgument && argument.receiver.stableType.isFunctionTypeOrSubtype
|
||||
|
||||
override fun convertParameterType(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
parameter: ParameterDescriptor,
|
||||
expectedParameterType: UnwrappedType
|
||||
): UnwrappedType? {
|
||||
val nonSuspendParameterType = createFunctionType(
|
||||
candidate.callComponents.builtIns,
|
||||
expectedParameterType.annotations,
|
||||
expectedParameterType.getReceiverTypeFromFunctionType(),
|
||||
expectedParameterType.getValueParameterTypesFromFunctionType().map { it.type },
|
||||
parameterNames = null,
|
||||
expectedParameterType.getReturnTypeFromFunctionType(),
|
||||
suspendFunction = false
|
||||
)
|
||||
|
||||
candidate.resolvedCall.registerArgumentWithSuspendConversion(argument, nonSuspendParameterType)
|
||||
|
||||
return nonSuspendParameterType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun KotlinResolutionCandidate.getExpectedTypeWithSuspendConversion(
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor
|
||||
): UnwrappedType? {
|
||||
if (SuspendTypeConversions.conversionDefinitelyNotNeeded(this, argument, candidateParameter)) return null
|
||||
|
||||
val parameterType = candidateParameter.type
|
||||
val nonSuspendParameterType = createFunctionType(
|
||||
callComponents.builtIns,
|
||||
parameterType.annotations,
|
||||
parameterType.getReceiverTypeFromFunctionType(),
|
||||
parameterType.getValueParameterTypesFromFunctionType().map { it.type },
|
||||
parameterNames = null,
|
||||
parameterType.getReturnTypeFromFunctionType(),
|
||||
suspendFunction = false
|
||||
)
|
||||
|
||||
resolvedCall.registerArgumentWithSuspendConversion(argument, nonSuspendParameterType)
|
||||
|
||||
return nonSuspendParameterType
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.descriptors.ParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
|
||||
interface ParameterTypeConversion {
|
||||
fun conversionDefinitelyNotNeeded(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
expectedParameterType: UnwrappedType
|
||||
): Boolean
|
||||
|
||||
fun conversionIsNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean
|
||||
fun conversionIsNeededAfterSubtypingCheck(argument: KotlinCallArgument): Boolean
|
||||
|
||||
fun convertParameterType(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
parameter: ParameterDescriptor,
|
||||
expectedParameterType: UnwrappedType
|
||||
): UnwrappedType?
|
||||
}
|
||||
|
||||
object TypeConversions {
|
||||
fun performCompositeConversionBeforeSubtyping(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor,
|
||||
candidateExpectedType: UnwrappedType,
|
||||
): ConversionData {
|
||||
val samConversionData = performConversionBeforeSubtyping(
|
||||
candidate, argument, candidateParameter, candidateExpectedType, SamTypeConversions
|
||||
)
|
||||
val suspendConversionData = if (samConversionData.convertedType == null) {
|
||||
performConversionBeforeSubtyping(candidate, argument, candidateParameter, candidateExpectedType, SuspendTypeConversions)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return ConversionData(
|
||||
convertedType = samConversionData.convertedType ?: suspendConversionData?.convertedType,
|
||||
wasConversion = samConversionData.wasConversion || suspendConversionData?.wasConversion == true,
|
||||
conversionDefinitelyNotNeeded = samConversionData.conversionDefinitelyNotNeeded &&
|
||||
(suspendConversionData == null || suspendConversionData.conversionDefinitelyNotNeeded)
|
||||
)
|
||||
}
|
||||
|
||||
fun performCompositeConversionAfterSubtyping(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor,
|
||||
candidateExpectedType: UnwrappedType,
|
||||
): UnwrappedType? {
|
||||
val samConvertedType = performConversionAfterSubtyping(
|
||||
candidate, argument, candidateParameter, candidateExpectedType, SamTypeConversions
|
||||
)
|
||||
|
||||
if (samConvertedType != null) return samConvertedType
|
||||
|
||||
return performConversionAfterSubtyping(candidate, argument, candidateParameter, candidateExpectedType, SuspendTypeConversions)
|
||||
}
|
||||
|
||||
private fun performConversionAfterSubtyping(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor,
|
||||
candidateExpectedType: UnwrappedType,
|
||||
conversion: ParameterTypeConversion
|
||||
): UnwrappedType? {
|
||||
return if (
|
||||
conversion.conversionIsNeededAfterSubtypingCheck(argument) &&
|
||||
!conversion.conversionDefinitelyNotNeeded(candidate, argument, candidateExpectedType)
|
||||
) {
|
||||
conversion.convertParameterType(candidate, argument, candidateParameter, candidateExpectedType)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun performConversionBeforeSubtyping(
|
||||
candidate: KotlinResolutionCandidate,
|
||||
argument: KotlinCallArgument,
|
||||
candidateParameter: ParameterDescriptor,
|
||||
candidateExpectedType: UnwrappedType,
|
||||
conversion: ParameterTypeConversion
|
||||
): ConversionData {
|
||||
val conversionDefinitelyNotNeeded = conversion.conversionDefinitelyNotNeeded(candidate, argument, candidateExpectedType)
|
||||
return if (!conversionDefinitelyNotNeeded && conversion.conversionIsNeededBeforeSubtypingCheck(argument)) {
|
||||
ConversionData(
|
||||
conversion.convertParameterType(candidate, argument, candidateParameter, candidateExpectedType),
|
||||
wasConversion = true,
|
||||
conversionDefinitelyNotNeeded
|
||||
)
|
||||
} else {
|
||||
ConversionData(convertedType = null, wasConversion = false, conversionDefinitelyNotNeeded)
|
||||
}
|
||||
}
|
||||
|
||||
class ConversionData(val convertedType: UnwrappedType?, val wasConversion: Boolean, val conversionDefinitelyNotNeeded: Boolean)
|
||||
}
|
||||
Reference in New Issue
Block a user