Introduce limited constant conversions for Kotlin/Native

#KT-25320 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-07-16 15:59:21 +03:00
parent 1a51132884
commit acd5b62148
15 changed files with 735 additions and 37 deletions
@@ -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),
@@ -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
)
)
}