Introduce Unit-conversions for simple arguments

This commit is contained in:
Mikhail Zarechenskiy
2020-06-02 15:04:39 +03:00
parent f08a45f2d4
commit 71cbe97688
23 changed files with 484 additions and 6 deletions
@@ -0,0 +1,83 @@
/*
* 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.builtins.*
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.inference.model.LowerPriorityToPreserveCompatibility
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallArgument
import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.model.SimpleKotlinCallArgument
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.types.typeUtil.isUnit
object UnitTypeConversions : ParameterTypeConversion {
override fun conversionDefinitelyNotNeeded(
candidate: KotlinResolutionCandidate,
argument: KotlinCallArgument,
expectedParameterType: UnwrappedType
): Boolean {
// for callable references and lambdas it already works
if (argument !is SimpleKotlinCallArgument) return true
val receiver = argument.receiver
if (receiver.receiverValue.type.hasUnitOrDynamicReturnType()) return true
if (receiver.typesFromSmartCasts.any { it.hasUnitOrDynamicReturnType() }) return true
if (
!expectedParameterType.isBuiltinFunctionalType ||
!expectedParameterType.getReturnTypeFromFunctionType().isUnit()
) return true
return false
}
private fun KotlinType.hasUnitOrDynamicReturnType(): Boolean =
isFunctionOrKFunctionTypeWithAnySuspendability && arguments.last().type.isUnitOrDynamic()
private fun KotlinType.isUnitOrDynamic(): Boolean = isUnit() || isDynamic()
override fun conversionIsNeededBeforeSubtypingCheck(argument: KotlinCallArgument): Boolean =
argument is SimpleKotlinCallArgument && argument.receiver.stableType.isFunctionType
override fun conversionIsNeededAfterSubtypingCheck(argument: KotlinCallArgument): Boolean {
if (argument !is SimpleKotlinCallArgument) return false
var isFunctionTypeOrSubtype = false
val hasReturnTypeInSubtypes = argument.receiver.stableType.isFunctionTypeOrSubtype {
isFunctionTypeOrSubtype = true
it.getReturnTypeFromFunctionType().isUnitOrDynamic()
}
if (!isFunctionTypeOrSubtype) return false
return !hasReturnTypeInSubtypes
}
override fun convertParameterType(
candidate: KotlinResolutionCandidate,
argument: KotlinCallArgument,
parameter: ParameterDescriptor,
expectedParameterType: UnwrappedType
): UnwrappedType? {
val nonUnitReturnedParameterType = createFunctionType(
candidate.callComponents.builtIns,
expectedParameterType.annotations,
expectedParameterType.getReceiverTypeFromFunctionType(),
expectedParameterType.getValueParameterTypesFromFunctionType().map { it.type },
parameterNames = null,
candidate.callComponents.builtIns.nullableAnyType,
suspendFunction = expectedParameterType.isSuspendFunctionType
)
candidate.addDiagnostic(LowerPriorityToPreserveCompatibility)
return nonUnitReturnedParameterType
}
}
@@ -40,14 +40,23 @@ object TypeConversions {
)
val suspendConversionData = performConversionBeforeSubtyping(
candidate, argument, candidateParameter, samConversionData.convertedType ?: candidateExpectedType, SuspendTypeConversions
candidate, argument, candidateParameter,
candidateExpectedType = samConversionData.convertedType ?: candidateExpectedType,
SuspendTypeConversions
)
val unitConversionData = performConversionBeforeSubtyping(
candidate, argument, candidateParameter,
candidateExpectedType = suspendConversionData.convertedType ?: samConversionData.convertedType ?: candidateExpectedType,
UnitTypeConversions
)
return ConversionData(
convertedType = suspendConversionData.convertedType ?: samConversionData.convertedType,
wasConversion = samConversionData.wasConversion || suspendConversionData.wasConversion,
convertedType = unitConversionData.convertedType ?: suspendConversionData.convertedType ?: samConversionData.convertedType,
wasConversion = samConversionData.wasConversion || suspendConversionData.wasConversion || unitConversionData.wasConversion,
conversionDefinitelyNotNeeded = samConversionData.conversionDefinitelyNotNeeded &&
suspendConversionData.conversionDefinitelyNotNeeded
suspendConversionData.conversionDefinitelyNotNeeded &&
unitConversionData.conversionDefinitelyNotNeeded
)
}
@@ -60,10 +69,13 @@ object TypeConversions {
val samConvertedType = performConversionAfterSubtyping(
candidate, argument, candidateParameter, candidateExpectedType, SamTypeConversions
)
if (samConvertedType != null) return samConvertedType
return performConversionAfterSubtyping(candidate, argument, candidateParameter, candidateExpectedType, SuspendTypeConversions)
val suspendConvertedType =
performConversionAfterSubtyping(candidate, argument, candidateParameter, candidateExpectedType, SuspendTypeConversions)
if (suspendConvertedType != null) return suspendConvertedType
return performConversionAfterSubtyping(candidate, argument, candidateParameter, candidateExpectedType, UnitTypeConversions)
}
private fun performConversionAfterSubtyping(