Introduce limited constant conversions for Kotlin/Native
#KT-25320 Fixed
This commit is contained in:
@@ -31,6 +31,8 @@ val JVM_FIELD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmField")
|
||||
|
||||
val JVM_DEFAULT_FQ_NAME = FqName("kotlin.jvm.JvmDefault")
|
||||
|
||||
private val IMPLICIT_INTEGER_COERCION_ANNOTATION_FQ_NAME = FqName("kotlin.internal.ImplicitIntegerCoercion")
|
||||
|
||||
fun CallableMemberDescriptor.hasJvmDefaultAnnotation() =
|
||||
DescriptorUtils.getDirectMember(this).annotations.hasAnnotation(JVM_DEFAULT_FQ_NAME)
|
||||
|
||||
@@ -39,6 +41,10 @@ fun DeclarationDescriptor.hasJvmStaticAnnotation(): Boolean {
|
||||
return annotations.findAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) != null
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.hasImplicitIntegerCoercionAnnotation(): Boolean {
|
||||
return annotations.findAnnotation(IMPLICIT_INTEGER_COERCION_ANNOTATION_FQ_NAME) != null
|
||||
}
|
||||
|
||||
private val JVM_SYNTHETIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSynthetic")
|
||||
|
||||
fun DeclarationDescriptor.hasJvmSyntheticAnnotation() = findJvmSyntheticAnnotation() != null
|
||||
|
||||
@@ -44,10 +44,7 @@ import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsUtil;
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor;
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
|
||||
import org.jetbrains.kotlin.types.FunctionPlaceholders;
|
||||
import org.jetbrains.kotlin.types.FunctionPlaceholdersKt;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker;
|
||||
import org.jetbrains.kotlin.types.expressions.*;
|
||||
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
|
||||
@@ -392,9 +389,21 @@ public class ArgumentTypeResolver {
|
||||
@NotNull KtExpression expression
|
||||
) {
|
||||
KotlinType type = trace.getType(expression);
|
||||
if (type != null && !type.getConstructor().isDenotable()) {
|
||||
if (type.getConstructor() instanceof IntegerValueTypeConstructor) {
|
||||
IntegerValueTypeConstructor constructor = (IntegerValueTypeConstructor) type.getConstructor();
|
||||
return type != null ? updateResultArgumentTypeIfNotDenotable(trace, statementFilter, expectedType, type, expression) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public KotlinType updateResultArgumentTypeIfNotDenotable(
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull StatementFilter statementFilter,
|
||||
@NotNull KotlinType expectedType,
|
||||
@NotNull KotlinType targetType,
|
||||
@NotNull KtExpression expression
|
||||
) {
|
||||
TypeConstructor typeConstructor = targetType.getConstructor();
|
||||
if (!typeConstructor.isDenotable()) {
|
||||
if (typeConstructor instanceof IntegerValueTypeConstructor) {
|
||||
IntegerValueTypeConstructor constructor = (IntegerValueTypeConstructor) typeConstructor;
|
||||
KotlinType primitiveType = TypeUtils.getPrimitiveNumberType(constructor, expectedType);
|
||||
constantExpressionEvaluator.updateNumberType(primitiveType, expression, statementFilter, trace);
|
||||
return primitiveType;
|
||||
|
||||
@@ -20,9 +20,12 @@ import org.jetbrains.kotlin.builtins.*
|
||||
import org.jetbrains.kotlin.contracts.EffectSystem
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.CONSTRAINT_SYSTEM_COMPLETER
|
||||
import org.jetbrains.kotlin.resolve.annotations.hasImplicitIntegerCoercionAnnotation
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.ResolveArgumentsMode.RESOLVE_FUNCTION_ARGUMENTS
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInvokeCallOnVariable
|
||||
@@ -46,11 +49,11 @@ import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstructor
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.expressions.DataFlowAnalyzer
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import java.util.*
|
||||
|
||||
class CallCompleter(
|
||||
@@ -279,28 +282,50 @@ class CallCompleter(
|
||||
|
||||
for (valueArgument in context.call.valueArguments) {
|
||||
val argumentMapping = getArgumentMapping(valueArgument!!)
|
||||
val (expectedType, callPosition) = when (argumentMapping) {
|
||||
is ArgumentMatch -> Pair(
|
||||
getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument, context),
|
||||
CallPosition.ValueArgumentPosition(results.resultingCall, argumentMapping.valueParameter, valueArgument)
|
||||
)
|
||||
else -> Pair(TypeUtils.NO_EXPECTED_TYPE, CallPosition.Unknown)
|
||||
|
||||
val expectedType: KotlinType
|
||||
val callPosition: CallPosition
|
||||
val parameter: ValueParameterDescriptor?
|
||||
when (argumentMapping) {
|
||||
is ArgumentMatch -> {
|
||||
expectedType = getEffectiveExpectedType(argumentMapping.valueParameter, valueArgument, context)
|
||||
callPosition = CallPosition.ValueArgumentPosition(results.resultingCall, argumentMapping.valueParameter, valueArgument)
|
||||
parameter = argumentMapping.valueParameter
|
||||
}
|
||||
else -> {
|
||||
expectedType = TypeUtils.NO_EXPECTED_TYPE
|
||||
callPosition = CallPosition.Unknown
|
||||
parameter = null
|
||||
}
|
||||
}
|
||||
|
||||
val newContext =
|
||||
context.replaceDataFlowInfo(getDataFlowInfoForArgument(valueArgument))
|
||||
.replaceExpectedType(expectedType)
|
||||
.replaceCallPosition(callPosition)
|
||||
completeOneArgument(valueArgument, newContext)
|
||||
completeOneArgument(valueArgument, parameter, newContext)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTypeForConvertableConstant(constant: CompileTimeConstant<*>): SimpleType? {
|
||||
val value = constant.getValue(TypeUtils.NO_EXPECTED_TYPE).safeAs<Number>()?.toLong() ?: return null
|
||||
val typeConstructor = IntegerValueTypeConstructor(
|
||||
value, moduleDescriptor, constant.parameters
|
||||
)
|
||||
return KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(
|
||||
Annotations.EMPTY, typeConstructor, emptyList(), false,
|
||||
ErrorUtils.createErrorScope("Scope for number value type ($typeConstructor)", true)
|
||||
)
|
||||
}
|
||||
|
||||
private fun completeOneArgument(
|
||||
valueArgument: ValueArgument,
|
||||
argument: ValueArgument,
|
||||
parameter: ValueParameterDescriptor?,
|
||||
context: BasicCallResolutionContext
|
||||
) {
|
||||
if (valueArgument.isExternal()) return
|
||||
if (argument.isExternal()) return
|
||||
|
||||
val expression = valueArgument.getArgumentExpression() ?: return
|
||||
val expression = argument.getArgumentExpression() ?: return
|
||||
val deparenthesized = KtPsiUtil.getLastElementDeparenthesized(expression, context.statementFilter) ?: return
|
||||
|
||||
val recordedType = context.trace.getType(expression)
|
||||
@@ -320,6 +345,18 @@ class CallCompleter(
|
||||
updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(context, expression) ?: updatedType
|
||||
}
|
||||
|
||||
if (parameter?.hasImplicitIntegerCoercionAnnotation() == true) {
|
||||
val argumentCompileTimeValue = context.trace[BindingContext.COMPILE_TIME_VALUE, deparenthesized]
|
||||
if (argumentCompileTimeValue != null && argumentCompileTimeValue.parameters.isConvertableConstVal) {
|
||||
val generalNumberType = createTypeForConvertableConstant(argumentCompileTimeValue)
|
||||
if (generalNumberType != null) {
|
||||
updatedType = argumentTypeResolver.updateResultArgumentTypeIfNotDenotable(
|
||||
context.trace, context.statementFilter, context.expectedType, generalNumberType, expression
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatedType = updateRecordedTypeForArgument(updatedType, recordedType, expression, context.statementFilter, context.trace)
|
||||
|
||||
// While the expected type is not known, the function literal arguments are not analyzed (to analyze function literal bodies once),
|
||||
|
||||
+26
-8
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext.COLLECTION_LITERAL_CALL
|
||||
import org.jetbrains.kotlin.resolve.annotations.hasImplicitIntegerCoercionAnnotation
|
||||
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.getEffectiveExpectedType
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -401,7 +402,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
isUnsignedNumberLiteral = isUnsigned,
|
||||
isUnsignedLongNumberLiteral = isUnsignedLong,
|
||||
usesVariableAsConstant = false,
|
||||
usesNonConstValAsConstant = false
|
||||
usesNonConstValAsConstant = false,
|
||||
isConvertableConstVal = false
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -450,7 +452,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
isUnsignedLongNumberLiteral = false,
|
||||
canBeUsedInAnnotation = canBeUsedInAnnotation,
|
||||
usesVariableAsConstant = usesVariableAsConstant,
|
||||
usesNonConstValAsConstant = usesNonConstantVariableAsConstant
|
||||
usesNonConstValAsConstant = usesNonConstantVariableAsConstant,
|
||||
isConvertableConstVal = false
|
||||
)
|
||||
)
|
||||
else null
|
||||
@@ -513,7 +516,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
isUnsignedNumberLiteral = false,
|
||||
isUnsignedLongNumberLiteral = false,
|
||||
usesVariableAsConstant = leftConstant.usesVariableAsConstant || rightConstant.usesVariableAsConstant,
|
||||
usesNonConstValAsConstant = leftConstant.usesNonConstValAsConstant || rightConstant.usesNonConstValAsConstant
|
||||
usesNonConstValAsConstant = leftConstant.usesNonConstValAsConstant || rightConstant.usesNonConstValAsConstant,
|
||||
isConvertableConstVal = false
|
||||
)
|
||||
)
|
||||
} else {
|
||||
@@ -560,7 +564,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
canBeUsedInAnnotation,
|
||||
!isNumberConversionMethod && isArgumentPure,
|
||||
false, false,
|
||||
usesVariableAsConstant, usesNonConstValAsConstant
|
||||
usesVariableAsConstant, usesNonConstValAsConstant,
|
||||
false
|
||||
)
|
||||
)
|
||||
} else if (argumentsEntrySet.size == 1) {
|
||||
@@ -592,7 +597,7 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
val usesNonConstValAsConstant =
|
||||
usesNonConstValAsConstant(argumentForReceiver.expression) || usesNonConstValAsConstant(argumentForParameter.expression)
|
||||
val parameters = CompileTimeConstant.Parameters(
|
||||
canBeUsedInAnnotation, areArgumentsPure, false, false, usesVariableAsConstant, usesNonConstValAsConstant
|
||||
canBeUsedInAnnotation, areArgumentsPure, false, false, usesVariableAsConstant, usesNonConstValAsConstant, false
|
||||
)
|
||||
return when (resultingDescriptorName) {
|
||||
OperatorNameConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression)?.wrap(parameters)
|
||||
@@ -705,6 +710,11 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
// TODO: FIXME: see KT-10425
|
||||
if (callableDescriptor is PropertyDescriptor && callableDescriptor.modality != Modality.FINAL) return null
|
||||
|
||||
val isConvertableConstVal =
|
||||
callableDescriptor.isConst &&
|
||||
callableDescriptor.hasImplicitIntegerCoercionAnnotation() &&
|
||||
callableDescriptor.compileTimeInitializer is IntValue
|
||||
|
||||
return callableDescriptor.compileTimeInitializer?.wrap(
|
||||
CompileTimeConstant.Parameters(
|
||||
canBeUsedInAnnotation = isPropertyCompileTimeConstant(callableDescriptor),
|
||||
@@ -712,7 +722,8 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
isUnsignedNumberLiteral = false,
|
||||
isUnsignedLongNumberLiteral = false,
|
||||
usesVariableAsConstant = true,
|
||||
usesNonConstValAsConstant = !callableDescriptor.isConst
|
||||
usesNonConstValAsConstant = !callableDescriptor.isConst,
|
||||
isConvertableConstVal = isConvertableConstVal
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -973,11 +984,18 @@ private class ConstantExpressionEvaluatorVisitor(
|
||||
isUnsigned: Boolean = false,
|
||||
isUnsignedLong: Boolean = false,
|
||||
usesVariableAsConstant: Boolean = false,
|
||||
usesNonConstValAsConstant: Boolean = false
|
||||
usesNonConstValAsConstant: Boolean = false,
|
||||
isConvertableConstVal: Boolean = false
|
||||
): TypedCompileTimeConstant<T> =
|
||||
wrap(
|
||||
CompileTimeConstant.Parameters(
|
||||
canBeUsedInAnnotation, isPure, isUnsigned, isUnsignedLong, usesVariableAsConstant, usesNonConstValAsConstant
|
||||
canBeUsedInAnnotation,
|
||||
isPure,
|
||||
isUnsigned,
|
||||
isUnsignedLong,
|
||||
usesVariableAsConstant,
|
||||
usesNonConstValAsConstant,
|
||||
isConvertableConstVal
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user