Split CompileTimeConstant into two entities

1. ConstantValue
	* just holds some value and its type
	* implementations for concrete constants
2. CompileTimeConstant
	* is only produced by ConstantExpressionEvaluator
	* has additional flags (canBeUsedInAnnotation etc)
	* has two implementations TypedCompileTimeConstant containing a constant value
		and IntegerValueConstant which does not have exact type
	* can be converted to ConstantValue

Adjustt usages to use ConstantValue if flags are not needed
Add tests for some uncovered cases
This commit is contained in:
Pavel V. Talanov
2015-07-07 14:56:19 +03:00
parent 155f00578d
commit c313887641
134 changed files with 791 additions and 907 deletions
@@ -44,6 +44,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
@@ -210,16 +211,16 @@ public class AnnotationResolver {
}
@NotNull
public static Map<ValueParameterDescriptor, CompileTimeConstant<?>> resolveAnnotationArguments(
public static Map<ValueParameterDescriptor, ConstantValue<?>> resolveAnnotationArguments(
@NotNull ResolvedCall<?> resolvedCall,
@NotNull BindingTrace trace
) {
Map<ValueParameterDescriptor, CompileTimeConstant<?>> arguments = new HashMap<ValueParameterDescriptor, CompileTimeConstant<?>>();
Map<ValueParameterDescriptor, ConstantValue<?>> arguments = new HashMap<ValueParameterDescriptor, ConstantValue<?>>();
for (Map.Entry<ValueParameterDescriptor, ResolvedValueArgument> descriptorToArgument : resolvedCall.getValueArguments().entrySet()) {
ValueParameterDescriptor parameterDescriptor = descriptorToArgument.getKey();
ResolvedValueArgument resolvedArgument = descriptorToArgument.getValue();
CompileTimeConstant<?> value = getAnnotationArgumentValue(trace, parameterDescriptor, resolvedArgument);
ConstantValue<?> value = getAnnotationArgumentValue(trace, parameterDescriptor, resolvedArgument);
if (value != null) {
arguments.put(parameterDescriptor, value);
}
@@ -228,32 +229,30 @@ public class AnnotationResolver {
}
@Nullable
public static CompileTimeConstant<?> getAnnotationArgumentValue(
public static ConstantValue<?> getAnnotationArgumentValue(
BindingTrace trace,
ValueParameterDescriptor parameterDescriptor,
ResolvedValueArgument resolvedArgument
) {
JetType varargElementType = parameterDescriptor.getVarargElementType();
boolean argumentsAsVararg = varargElementType != null && !hasSpread(resolvedArgument);
List<CompileTimeConstant<?>> constants = resolveValueArguments(
resolvedArgument, argumentsAsVararg ? varargElementType : parameterDescriptor.getType(), trace);
final JetType constantType = argumentsAsVararg ? varargElementType : parameterDescriptor.getType();
List<CompileTimeConstant<?>> compileTimeConstants = resolveValueArguments(resolvedArgument, constantType, trace);
List<ConstantValue<?>> constants = KotlinPackage.map(compileTimeConstants, new Function1<CompileTimeConstant<?>, ConstantValue<?>>() {
@Override
public ConstantValue<?> invoke(CompileTimeConstant<?> constant) {
return constant.toConstantValue(constantType);
}
});
if (argumentsAsVararg) {
if (parameterDescriptor.declaresDefaultValue() && compileTimeConstants.isEmpty()) return null;
boolean usesVariableAsConstant = KotlinPackage.any(constants, new Function1<CompileTimeConstant<?>, Boolean>() {
@Override
public Boolean invoke(CompileTimeConstant<?> constant) {
return constant.usesVariableAsConstant();
}
});
if (parameterDescriptor.declaresDefaultValue() && constants.isEmpty()) return null;
return new ArrayValue(constants, parameterDescriptor.getType(), usesVariableAsConstant);
return new ArrayValue(constants, parameterDescriptor.getType());
}
else {
// we should actually get only one element, but just in case of getting many, we take the last one
return !constants.isEmpty() ? KotlinPackage.last(constants) : null;
return KotlinPackage.lastOrNull(constants);
}
}
@@ -280,7 +279,7 @@ public class AnnotationResolver {
}
CompileTimeConstant<?> constant = ConstantExpressionEvaluator.getConstant(argumentExpression, trace.getBindingContext());
if (constant != null && constant.canBeUsedInAnnotations()) {
if (constant != null && constant.getCanBeUsedInAnnotations()) {
return;
}
@@ -33,7 +33,6 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
import org.jetbrains.kotlin.resolve.scopes.*;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
@@ -735,8 +734,7 @@ public class BodyResolver {
JetScope propertyDeclarationInnerScope = JetScopeUtils.getPropertyDeclarationInnerScopeForInitializer(
propertyDescriptor, scope, propertyDescriptor.getTypeParameters(), NO_RECEIVER_PARAMETER, trace);
JetType expectedTypeForInitializer = property.getTypeReference() != null ? propertyDescriptor.getType() : NO_EXPECTED_TYPE;
CompileTimeConstant<?> compileTimeInitializer = propertyDescriptor.getCompileTimeInitializer();
if (compileTimeInitializer == null) {
if (propertyDescriptor.getCompileTimeInitializer() == null) {
expressionTypingServices.getType(propertyDeclarationInnerScope, initializer, expectedTypeForInitializer,
outerDataFlowInfo, trace);
}
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.psi.JetTypeReference;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.constants.BooleanValue;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.TypeProjection;
@@ -113,7 +115,7 @@ public class CompileTimeConstantUtils {
annotatedDescriptor.getAnnotations().findAnnotation(new FqName("kotlin.jvm.internal.Intrinsic"));
if (intrinsicAnnotation == null) return null;
Collection<CompileTimeConstant<?>> values = intrinsicAnnotation.getAllValueArguments().values();
Collection<ConstantValue<?>> values = intrinsicAnnotation.getAllValueArguments().values();
if (values.isEmpty()) return null;
Object value = values.iterator().next().getValue();
@@ -132,9 +134,13 @@ public class CompileTimeConstantUtils {
if (expression == null) return false;
CompileTimeConstant<?> compileTimeConstant =
ConstantExpressionEvaluator.evaluate(expression, trace, KotlinBuiltIns.getInstance().getBooleanType());
if (!(compileTimeConstant instanceof BooleanValue) || compileTimeConstant.usesVariableAsConstant()) return false;
if (!(compileTimeConstant instanceof TypedCompileTimeConstant) || compileTimeConstant.getUsesVariableAsConstant()) return false;
Boolean value = ((BooleanValue) compileTimeConstant).getValue();
ConstantValue constantValue = ((TypedCompileTimeConstant) compileTimeConstant).getConstantValue();
if (!(constantValue instanceof BooleanValue)) return false;
Boolean value = ((BooleanValue) constantValue).getValue();
return expectedValue == null || expectedValue.equals(value);
}
@@ -37,10 +37,8 @@ import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.constants.evaluate.EvaluatePackage;
import org.jetbrains.kotlin.resolve.dataClassUtils.DataClassUtilsPackage;
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil;
import org.jetbrains.kotlin.resolve.scopes.JetScope;
@@ -868,17 +866,13 @@ public class DescriptorResolver {
if (!variable.hasInitializer()) return;
variableDescriptor.setCompileTimeInitializer(
storageManager.createRecursionTolerantNullableLazyValue(new Function0<CompileTimeConstant<?>>() {
storageManager.createRecursionTolerantNullableLazyValue(new Function0<ConstantValue<?>>() {
@Nullable
@Override
public CompileTimeConstant<?> invoke() {
public ConstantValue<?> invoke() {
JetExpression initializer = variable.getInitializer();
JetType initializerType = expressionTypingServices.safeGetType(scope, initializer, variableType, dataFlowInfo, trace);
CompileTimeConstant<?> constant = ConstantExpressionEvaluator.evaluate(initializer, trace, initializerType);
if (constant instanceof IntegerValueTypeConstant) {
return EvaluatePackage.createCompileTimeConstantWithType((IntegerValueTypeConstant) constant, initializerType);
}
return constant;
return ConstantExpressionEvaluator.evaluateToConstantValue(initializer, trace, initializerType);
}
}, null)
);
@@ -26,13 +26,13 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.kotlin.diagnostics.*;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.StringValue;
import java.util.*;
@@ -302,9 +302,9 @@ public class ModifiersChecker {
}
String value = null;
Collection<CompileTimeConstant<?>> values = annotation.getAllValueArguments().values();
Collection<ConstantValue<?>> values = annotation.getAllValueArguments().values();
if (!values.isEmpty()) {
CompileTimeConstant<?> name = values.iterator().next();
ConstantValue<?> name = values.iterator().next();
if (name instanceof StringValue) {
value = ((StringValue) name).getValue();
}
@@ -39,7 +39,6 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.IntegerValueConstant;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.scopes.receivers.*;
import org.jetbrains.kotlin.types.ErrorUtils;
@@ -369,7 +368,7 @@ public class CallExpressionResolver {
}
CompileTimeConstant<?> value = ConstantExpressionEvaluator.evaluate(expression, context.trace, context.expectedType);
if (value instanceof IntegerValueConstant && ((IntegerValueConstant) value).isPure()) {
if (value != null && value.getIsPure()) {
return ExpressionTypingUtils.createCompileTimeConstantTypeInfo(value, expression, context);
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.resolve.calls.util
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import org.jetbrains.kotlin.resolve.descriptorUtil.classObjectType
import org.jetbrains.kotlin.resolve.descriptorUtil.getClassObjectReferenceTarget
import org.jetbrains.kotlin.resolve.descriptorUtil.hasClassObjectType
@@ -59,7 +58,7 @@ public class FakeCallableDescriptorForObject(
override fun getOriginal(): CallableDescriptor = this
override fun getCompileTimeInitializer(): CompileTimeConstant<out Any?>? = null
override fun getCompileTimeInitializer() = null
override fun getSource(): SourceElement = classDescriptor.getSource()
}
@@ -51,7 +51,7 @@ public class CompileTimeConstantChecker {
// return true if there is an error
public boolean checkConstantExpressionType(
@Nullable CompileTimeConstant compileTimeConstant,
@Nullable ConstantValue<?> compileTimeConstant,
@NotNull JetConstantExpression expression,
@NotNull JetType expectedType
) {
@@ -76,7 +76,7 @@ public class CompileTimeConstantChecker {
}
private boolean checkIntegerValue(
@Nullable CompileTimeConstant value,
@Nullable ConstantValue<?> value,
@NotNull JetType expectedType,
@NotNull JetConstantExpression expression
) {
@@ -98,7 +98,7 @@ public class CompileTimeConstantChecker {
}
private boolean checkFloatValue(
@Nullable CompileTimeConstant value,
@Nullable ConstantValue<?> value,
@NotNull JetType expectedType,
@NotNull JetConstantExpression expression
) {
@@ -125,7 +125,7 @@ public class CompileTimeConstantChecker {
return false;
}
private boolean checkCharValue(CompileTimeConstant<?> constant, JetType expectedType, JetConstantExpression expression) {
private boolean checkCharValue(ConstantValue<?> constant, JetType expectedType, JetConstantExpression expression) {
if (!noExpectedTypeOrError(expectedType)
&& !JetTypeChecker.DEFAULT.isSubtypeOf(builtIns.getCharType(), expectedType)) {
return reportError(CONSTANT_EXPECTED_TYPE_MISMATCH.on(expression, "character", expectedType));
@@ -41,31 +41,26 @@ import java.math.BigInteger
import kotlin.platform.platformStatic
public class ConstantExpressionEvaluator private constructor(val trace: BindingTrace) : JetVisitor<CompileTimeConstant<*>, JetType>() {
private val builtIns = KotlinBuiltIns.getInstance()
private val factory = ConstantValueFactory(KotlinBuiltIns.getInstance())
companion object {
platformStatic public fun evaluate(expression: JetExpression, trace: BindingTrace, expectedType: JetType? = TypeUtils.NO_EXPECTED_TYPE): CompileTimeConstant<*>? {
val evaluator = ConstantExpressionEvaluator(trace)
val constant = evaluator.evaluate(expression, expectedType)
return if (constant !is ErrorValue) constant else null
val constant = evaluator.evaluate(expression, expectedType) ?: return null
return if (!constant.isError) constant else null
}
platformStatic public fun isPropertyCompileTimeConstant(descriptor: VariableDescriptor): Boolean {
if (descriptor.isVar()) {
return false
}
if (DescriptorUtils.isObject(descriptor.getContainingDeclaration()) ||
DescriptorUtils.isStaticDeclaration(descriptor)) {
val returnType = descriptor.getType()
return KotlinBuiltIns.isPrimitiveType(returnType) || KotlinBuiltIns.isString(returnType)
}
return false
platformStatic public fun evaluateToConstantValue(
expression: JetExpression,
trace: BindingTrace,
expectedType: JetType
): ConstantValue<*>? {
return evaluate(expression, trace, expectedType)?.toConstantValue(expectedType)
}
platformStatic public fun getConstant(expression: JetExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
val constant = getPossiblyErrorConstant(expression, bindingContext)
return if (constant !is ErrorValue) constant else null
val constant = getPossiblyErrorConstant(expression, bindingContext) ?: return null
return if (!constant.isError) constant else null
}
platformStatic private fun getPossiblyErrorConstant(expression: JetExpression, bindingContext: BindingContext): CompileTimeConstant<*>? {
@@ -87,30 +82,38 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
return null
}
private val stringExpressionEvaluator = object : JetVisitor<StringValue, Nothing>() {
private val factory = CompileTimeConstantFactory(CompileTimeConstant.Parameters.Impl(true, false, false), builtIns)
private val stringExpressionEvaluator = object : JetVisitor<TypedCompileTimeConstant<String>, Nothing>() {
private fun createStringConstant(compileTimeConstant: CompileTimeConstant<*>): TypedCompileTimeConstant<String>? {
val constantValue = compileTimeConstant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE)
return when (constantValue) {
is ErrorValue, is EnumValue -> return null
is NullValue -> factory.createStringValue("null")
else -> factory.createStringValue(constantValue.value.toString())
}.wrap(compileTimeConstant.parameters)
}
fun evaluate(entry: JetStringTemplateEntry): StringValue? {
fun evaluate(entry: JetStringTemplateEntry): TypedCompileTimeConstant<String>? {
return entry.accept(this, null)
}
override fun visitStringTemplateEntryWithExpression(entry: JetStringTemplateEntryWithExpression, data: Nothing?): StringValue? {
val expression = entry.getExpression()
if (expression == null) return null
override fun visitStringTemplateEntryWithExpression(entry: JetStringTemplateEntryWithExpression, data: Nothing?): TypedCompileTimeConstant<String>? {
val expression = entry.getExpression() ?: return null
return createStringConstant(this@ConstantExpressionEvaluator.evaluate(expression, KotlinBuiltIns.getInstance().getStringType()))
return this@ConstantExpressionEvaluator.evaluate(expression, KotlinBuiltIns.getInstance().getStringType())?.let {
createStringConstant(it)
}
}
override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = factory.createStringValue(entry.getText())
override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = factory.createStringValue(entry.getText()).wrap()
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = factory.createStringValue(entry.getUnescapedValue())
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = factory.createStringValue(entry.getUnescapedValue()).wrap()
}
override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val text = expression.getText() ?: return null
val nodeElementType = expression.getNode().getElementType()
if (nodeElementType == JetNodeTypes.NULL) return NullValue(builtIns)
if (nodeElementType == JetNodeTypes.NULL) return factory.createNullValue().wrap()
val result: Any? = when (nodeElementType) {
JetNodeTypes.INTEGER_CONSTANT -> parseLong(text)
@@ -121,7 +124,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
} ?: return null
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(true, !isLongWithSuffix(), false))
return createConstant(result, expectedType, CompileTimeConstant.Parameters(true, !isLongWithSuffix(), false))
}
override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -152,17 +155,17 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
break
}
else {
if (!constant.canBeUsedInAnnotations()) canBeUsedInAnnotation = false
if (constant.usesVariableAsConstant()) usesVariableAsConstant = true
sb.append(constant.value)
if (!constant.canBeUsedInAnnotations) canBeUsedInAnnotation = false
if (constant.usesVariableAsConstant) usesVariableAsConstant = true
sb.append(constant.constantValue.value)
}
}
return if (!interupted)
createConstant(
sb.toString(),
expectedType,
CompileTimeConstant.Parameters.Impl(
isPure = true,
CompileTimeConstant.Parameters(
isPure = false,
canBeUsedInAnnotation = canBeUsedInAnnotation,
usesVariableAsConstant = usesVariableAsConstant
)
@@ -174,8 +177,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
evaluate(expression.getLeft(), expectedType)
override fun visitBinaryExpression(expression: JetBinaryExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val leftExpression = expression.getLeft()
if (leftExpression == null) return null
val leftExpression = expression.getLeft() ?: return null
val operationToken = expression.getOperationToken()
if (OperatorConventions.BOOLEAN_OPERATIONS.containsKey(operationToken)) {
@@ -183,14 +185,12 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val leftConstant = evaluate(leftExpression, booleanType)
if (leftConstant == null) return null
val rightExpression = expression.getRight()
if (rightExpression == null) return null
val rightExpression = expression.getRight() ?: return null
val rightConstant = evaluate(rightExpression, booleanType)
if (rightConstant == null) return null
val rightConstant = evaluate(rightExpression, booleanType) ?: return null
val leftValue = leftConstant.value
val rightValue = rightConstant.value
val leftValue = leftConstant.getValue(booleanType)
val rightValue = rightConstant.getValue(booleanType)
if (leftValue !is Boolean || rightValue !is Boolean) return null
val result = when (operationToken) {
@@ -198,8 +198,14 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
JetTokens.OROR -> leftValue || rightValue
else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}")
}
val usesVariableAsConstant = leftConstant.usesVariableAsConstant() || rightConstant.usesVariableAsConstant()
return createConstant(result, expectedType, CompileTimeConstant.Parameters.Impl(true, true, usesVariableAsConstant))
return createConstant(
result, expectedType,
CompileTimeConstant.Parameters(
canBeUsedInAnnotation = true,
isPure = false,
usesVariableAsConstant = leftConstant.usesVariableAsConstant || rightConstant.usesVariableAsConstant
)
)
}
else {
return evaluateCall(expression.getOperationReference(), leftExpression, expectedType)
@@ -226,7 +232,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
return createConstant(
result,
expectedType,
CompileTimeConstant.Parameters.Impl(
CompileTimeConstant.Parameters(
canBeUsedInAnnotation,
!isNumberConversionMethod && isArgumentPure,
usesVariableAsConstant)
@@ -240,8 +246,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) {
val parentExpression: JetExpression = PsiTreeUtil.getParentOfType(receiverExpression, javaClass())!!
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression))
//TODO_R:
return ErrorValue.create("Division by zero")
return factory.createErrorValue("Division by zero").wrap()
}
val result = evaluateBinaryAndCheck(argumentForReceiver, argumentForParameter, resultingDescriptorName.asString(), callExpression)
@@ -250,11 +255,10 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression)
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression) || usesVariableAsConstant(argumentForParameter.expression)
val parameters = CompileTimeConstant.Parameters.Impl(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant)
val factory = CompileTimeConstantFactory(parameters, builtIns)
val parameters = CompileTimeConstant.Parameters(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant)
return when (resultingDescriptorName) {
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, factory)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, factory)
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, factory)?.wrap(parameters)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, factory)?.wrap(parameters)
else -> {
createConstant(result, expectedType, parameters)
}
@@ -264,17 +268,11 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
return null
}
private fun usesVariableAsConstant(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.usesVariableAsConstant() ?: false
private fun usesVariableAsConstant(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.usesVariableAsConstant ?: false
private fun canBeUsedInAnnotation(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.canBeUsedInAnnotations() ?: false
private fun canBeUsedInAnnotation(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.canBeUsedInAnnotations ?: false
private fun isPureConstant(expression: JetExpression): Boolean {
val compileTimeConstant = getConstant(expression, trace.getBindingContext())
if (compileTimeConstant is IntegerValueConstant) {
return compileTimeConstant.isPure()
}
return false
}
private fun isPureConstant(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.isPure ?: false
private fun evaluateUnaryAndCheck(receiver: OperationArgument, name: String, callExpression: JetExpression): Any? {
val functions = unaryOperations[UnaryOperationKey(receiver.ctcType, name)]
@@ -342,25 +340,19 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
override fun visitSimpleNameExpression(expression: JetSimpleNameExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val enumDescriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, expression);
if (enumDescriptor != null && DescriptorUtils.isEnumEntry(enumDescriptor)) {
return EnumValue(enumDescriptor as ClassDescriptor)
return factory.createEnumValue(enumDescriptor as ClassDescriptor).wrap()
}
val resolvedCall = expression.getResolvedCall(trace.getBindingContext())
if (resolvedCall != null) {
val callableDescriptor = resolvedCall.getResultingDescriptor()
if (callableDescriptor is VariableDescriptor) {
val compileTimeConstant = callableDescriptor.getCompileTimeInitializer()
if (compileTimeConstant == null) return null
val variableInitializer = callableDescriptor.getCompileTimeInitializer() ?: return null
val value: Any? =
if (compileTimeConstant is IntegerValueTypeConstant)
compileTimeConstant.getValue(expectedType ?: TypeUtils.NO_EXPECTED_TYPE)
else
compileTimeConstant.value
return createConstant(
value,
variableInitializer.value,
expectedType,
CompileTimeConstant.Parameters.Impl(
CompileTimeConstant.Parameters(
canBeUsedInAnnotation = isPropertyCompileTimeConstant(callableDescriptor),
isPure = false,
usesVariableAsConstant = true
@@ -371,6 +363,18 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
return null
}
private fun isPropertyCompileTimeConstant(descriptor: VariableDescriptor): Boolean {
if (descriptor.isVar()) {
return false
}
if (DescriptorUtils.isObject(descriptor.getContainingDeclaration()) ||
DescriptorUtils.isStaticDeclaration(descriptor)) {
val returnType = descriptor.getType()
return KotlinBuiltIns.isPrimitiveType(returnType) || KotlinBuiltIns.isString(returnType)
}
return false
}
override fun visitQualifiedExpression(expression: JetQualifiedExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val selectorExpression = expression.getSelectorExpression()
// 1.toInt(); 1.plus(1);
@@ -409,7 +413,10 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
val arguments = call.getValueArguments().values().flatMap { resolveArguments(it.getArguments(), varargType) }
return ArrayValue(arguments, resultingDescriptor.getReturnType()!!, arguments.any() { it.usesVariableAsConstant() })
return ArrayValue(arguments.map { it.toConstantValue(varargType) }, resultingDescriptor.getReturnType()!!).
wrap(
usesVariableAsConstant = arguments.any { it.usesVariableAsConstant }
)
}
// Ann()
@@ -420,7 +427,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
classDescriptor.getDefaultType(),
AnnotationResolver.resolveAnnotationArguments(call, trace)
)
return AnnotationValue(descriptor)
return AnnotationValue(descriptor).wrap()
}
}
@@ -430,7 +437,7 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
override fun visitClassLiteralExpression(expression: JetClassLiteralExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val jetType = trace.getType(expression)!!
if (jetType.isError()) return null
return KClassValue(jetType)
return KClassValue(jetType).wrap()
}
private fun resolveArguments(valueArguments: List<ValueArgument>, expectedType: JetType): List<CompileTimeConstant<*>> {
@@ -476,32 +483,54 @@ public class ConstantExpressionEvaluator private constructor(val trace: BindingT
}
private fun createOperationArgument(expression: JetExpression, expressionType: JetType, compileTimeType: CompileTimeType<*>): OperationArgument? {
val evaluatedConstant = evaluate(expression, trace, expressionType)
if (evaluatedConstant == null) return null
if (evaluatedConstant is IntegerValueTypeConstant) {
val evaluationResultWithNewType = evaluatedConstant.getValue(expressionType)
return OperationArgument(evaluationResultWithNewType, compileTimeType, expression)
}
val evaluationResult = evaluatedConstant.value
if (evaluationResult == null) return null
val compileTimeConstant = evaluate(expression, trace, expressionType) ?: return null
val evaluationResult = compileTimeConstant.getValue(expressionType) ?: return null
return OperationArgument(evaluationResult, compileTimeType, expression)
}
fun createConstant(
private fun createConstant(
value: Any?,
expectedType: JetType?,
parameters: CompileTimeConstant.Parameters
): CompileTimeConstant<*>? {
return CompileTimeConstantFactory(parameters, builtIns).createCompileTimeConstant(value, if (parameters.isPure) expectedType ?: TypeUtils.NO_EXPECTED_TYPE else null)
return if (parameters.isPure) {
return createCompileTimeConstant(value, parameters, expectedType ?: TypeUtils.NO_EXPECTED_TYPE)
}
else {
factory.createConstantValue(value)?.wrap(parameters)
}
}
private fun createCompileTimeConstant(
value: Any?,
parameters: CompileTimeConstant.Parameters,
expectedType: JetType
): CompileTimeConstant<*>? {
return when (value) {
is Byte, is Short, is Int, is Long -> createIntegerCompileTimeConstant((value as Number).toLong(), parameters, expectedType)
else -> factory.createConstantValue(value)?.wrap(parameters)
}
}
private fun createIntegerCompileTimeConstant(
value: Long,
parameters: CompileTimeConstant.Parameters,
expectedType: JetType
): CompileTimeConstant<*>? {
if (TypeUtils.noExpectedType(expectedType) || expectedType.isError()) {
return IntegerValueTypeConstant(value, parameters)
}
val integerValue = factory.createIntegerConstantValue(value, expectedType)
if (integerValue != null) {
return integerValue.wrap(parameters)
}
return when (value) {
value.toInt().toLong() -> factory.createIntValue(value.toInt())
else -> factory.createLongValue(value)
}.wrap(parameters)
}
}
public fun IntegerValueTypeConstant.createCompileTimeConstantWithType(expectedType: JetType): CompileTimeConstant<*>?
= CompileTimeConstantFactory(CompileTimeConstant.Parameters.Impl(this.canBeUsedInAnnotations(), true, false), KotlinBuiltIns.getInstance()).createCompileTimeConstant(this.getValue(expectedType))
private fun hasLongSuffix(text: String) = text.endsWith('l') || text.endsWith('L')
public fun parseLong(text: String): Long? {
@@ -557,7 +586,7 @@ private fun parseBoolean(text: String): Boolean {
}
private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, factory: CompileTimeConstantFactory): CompileTimeConstant<*>? {
private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, factory: ConstantValueFactory): ConstantValue<*>? {
if (result is Boolean) {
assert(operationReference is JetSimpleNameExpression, "This method should be called only for equals operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
@@ -575,7 +604,7 @@ private fun createCompileTimeConstantForEquals(result: Any?, operationReference:
return null
}
private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, factory: CompileTimeConstantFactory): CompileTimeConstant<*>? {
private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, factory: ConstantValueFactory): ConstantValue<*>? {
if (result is Int) {
assert(operationReference is JetSimpleNameExpression, "This method should be called only for compareTo operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
@@ -594,19 +623,6 @@ private fun createCompileTimeConstantForCompareTo(result: Any?, operationReferen
return null
}
private fun createStringConstant(value: CompileTimeConstant<*>?): StringValue? {
return when (value) {
is IntegerValueTypeConstant -> CompileTimeConstantFactory(value.parameters, KotlinBuiltIns.getInstance()).createStringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString())
is StringValue -> value
is IntValue, is ByteValue, is ShortValue, is LongValue,
is CharValue,
is DoubleValue, is FloatValue,
is BooleanValue,
is NullValue -> CompileTimeConstantFactory(value.parameters, KotlinBuiltIns.getInstance()).createStringValue("${value.value}")
else -> null
}
}
fun isIntegerType(value: Any?) = value is Byte || value is Short || value is Int || value is Long
private fun getReceiverExpressionType(resolvedCall: ResolvedCall<*>): JetType? {
@@ -668,4 +684,3 @@ private fun <A> unaryOperation(
private data class BinaryOperationKey<A, B>(val f: CompileTimeType<out A>, val s: CompileTimeType<out B>, val functionName: String)
private data class UnaryOperationKey<A>(val f: CompileTimeType<out A>, val functionName: String)
@@ -35,7 +35,7 @@ import org.jetbrains.kotlin.diagnostics.Severity;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.StringValue;
import org.jetbrains.kotlin.util.ExtensionProvider;
@@ -214,9 +214,9 @@ public class DiagnosticsWithSuppression implements Diagnostics {
if (!KotlinBuiltIns.isSuppressAnnotation(annotationDescriptor)) continue;
// We only add strings and skip other values to facilitate recovery in presence of erroneous code
for (CompileTimeConstant<?> arrayValue : annotationDescriptor.getAllValueArguments().values()) {
for (ConstantValue<?> arrayValue : annotationDescriptor.getAllValueArguments().values()) {
if ((arrayValue instanceof ArrayValue)) {
for (CompileTimeConstant<?> value : ((ArrayValue) arrayValue).getValue()) {
for (ConstantValue<?> value : ((ArrayValue) arrayValue).getValue()) {
if (value instanceof StringValue) {
builder.add(String.valueOf(((StringValue) value).getValue()).toLowerCase());
}
@@ -32,7 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ArgumentMapping;
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.constants.ArrayValue;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.EnumValue;
import static kotlin.KotlinPackage.firstOrNull;
@@ -53,7 +53,7 @@ public class InlineUtil {
if (annotation == null) {
return InlineStrategy.NOT_INLINE;
}
CompileTimeConstant<?> argument = firstOrNull(annotation.getAllValueArguments().values());
ConstantValue<?> argument = firstOrNull(annotation.getAllValueArguments().values());
if (argument == null) {
return InlineStrategy.AS_FUNCTION;
}
@@ -72,9 +72,9 @@ public class InlineUtil {
private static boolean hasInlineOption(@NotNull ValueParameterDescriptor descriptor, @NotNull InlineOption option) {
AnnotationDescriptor annotation = descriptor.getAnnotations().findAnnotation(KotlinBuiltIns.FQ_NAMES.inlineOptions);
if (annotation != null) {
CompileTimeConstant<?> argument = firstOrNull(annotation.getAllValueArguments().values());
ConstantValue<?> argument = firstOrNull(annotation.getAllValueArguments().values());
if (argument instanceof ArrayValue) {
for (CompileTimeConstant<?> value : ((ArrayValue) argument).getValue()) {
for (ConstantValue<?> value : ((ArrayValue) argument).getValue()) {
if (value instanceof EnumValue && ((EnumValue) value).getValue().getName().asString().equals(option.name())) {
return true;
}
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.resolve.AnnotationResolver
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil
import org.jetbrains.kotlin.resolve.lazy.LazyEntity
import org.jetbrains.kotlin.resolve.scopes.JetScope
@@ -109,7 +109,7 @@ public class LazyAnnotationDescriptor(
override fun getAllValueArguments() = valueArguments()
private fun computeValueArguments(): Map<ValueParameterDescriptor, CompileTimeConstant<*>> {
private fun computeValueArguments(): Map<ValueParameterDescriptor, ConstantValue<*>> {
val resolutionResults = c.annotationResolver.resolveAnnotationCall(annotationEntry, c.scope, c.trace)
AnnotationResolver.checkAnnotationType(annotationEntry, c.trace, resolutionResults)
@@ -121,7 +121,7 @@ public class LazyAnnotationDescriptor(
if (resolvedArgument == null) null
else AnnotationResolver.getAnnotationArgumentValue(c.trace, valueParameter, resolvedArgument)
}
.filterValues { it != null } as Map<ValueParameterDescriptor, CompileTimeConstant<*>>
.filterValues { it != null } as Map<ValueParameterDescriptor, ConstantValue<*>>
}
override fun forceResolveAllContents() {
@@ -55,9 +55,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind;
import org.jetbrains.kotlin.resolve.calls.tasks.ResolutionCandidate;
import org.jetbrains.kotlin.resolve.calls.tasks.TracingStrategy;
import org.jetbrains.kotlin.resolve.calls.util.CallMaker;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstantChecker;
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.kotlin.resolve.constants.*;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.scopes.JetScope;
import org.jetbrains.kotlin.resolve.scopes.WritableScopeImpl;
@@ -121,19 +119,20 @@ public class BasicExpressionTypingVisitor extends ExpressionTypingVisitor {
@Override
public JetTypeInfo visitConstantExpression(@NotNull JetConstantExpression expression, ExpressionTypingContext context) {
CompileTimeConstant<?> value = ConstantExpressionEvaluator.evaluate(expression, context.trace, context.expectedType);
CompileTimeConstant<?> compileTimeConstant = ConstantExpressionEvaluator.evaluate(expression, context.trace, context.expectedType);
if (!(value instanceof IntegerValueTypeConstant)) {
if (!(compileTimeConstant instanceof IntegerValueTypeConstant)) {
CompileTimeConstantChecker compileTimeConstantChecker = context.getCompileTimeConstantChecker();
boolean hasError = compileTimeConstantChecker.checkConstantExpressionType(value, expression, context.expectedType);
ConstantValue constantValue = compileTimeConstant != null ? ((TypedCompileTimeConstant) compileTimeConstant).getConstantValue() : null;
boolean hasError = compileTimeConstantChecker.checkConstantExpressionType(constantValue, expression, context.expectedType);
if (hasError) {
IElementType elementType = expression.getNode().getElementType();
return TypeInfoFactoryPackage.createTypeInfo(getDefaultType(elementType), context);
}
}
assert value != null : "CompileTimeConstant should be evaluated for constant expression or an error should be recorded " + expression.getText();
return createCompileTimeConstantTypeInfo(value, expression, context);
assert compileTimeConstant != null : "CompileTimeConstant should be evaluated for constant expression or an error should be recorded " + expression.getText();
return createCompileTimeConstantTypeInfo(compileTimeConstant, expression, context);
}
@NotNull
@@ -31,11 +31,9 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory;
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstantChecker;
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.constants.evaluate.EvaluatePackage;
import org.jetbrains.kotlin.types.JetType;
import org.jetbrains.kotlin.types.TypeUtils;
import org.jetbrains.kotlin.types.TypesPackage;
@@ -176,12 +174,9 @@ public class DataFlowUtils {
}
if (expression instanceof JetConstantExpression) {
CompileTimeConstant<?> value = ConstantExpressionEvaluator.evaluate(expression, c.trace, c.expectedType);
if (value instanceof IntegerValueTypeConstant) {
value = EvaluatePackage.createCompileTimeConstantWithType((IntegerValueTypeConstant) value, c.expectedType);
}
ConstantValue<?> constantValue = ConstantExpressionEvaluator.evaluateToConstantValue(expression, c.trace, c.expectedType);
boolean error = new CompileTimeConstantChecker(c.trace, true)
.checkConstantExpressionType(value, (JetConstantExpression) expression, c.expectedType);
.checkConstantExpressionType(constantValue, (JetConstantExpression) expression, c.expectedType);
if (hasError != null) hasError.set(error);
return expressionType;
}
@@ -22,7 +22,6 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.diagnostics.Diagnostic;
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory;
@@ -33,6 +32,7 @@ import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant;
import org.jetbrains.kotlin.resolve.constants.TypedCompileTimeConstant;
import org.jetbrains.kotlin.resolve.scopes.WritableScope;
import org.jetbrains.kotlin.resolve.scopes.WritableScopeImpl;
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver;
@@ -248,10 +248,19 @@ public class ExpressionTypingUtils {
@NotNull JetExpression expression,
@NotNull ExpressionTypingContext context
) {
JetType expressionType = value.getType();
if (value instanceof IntegerValueTypeConstant && context.contextDependency == INDEPENDENT) {
expressionType = ((IntegerValueTypeConstant) value).getType(context.expectedType);
ArgumentTypeResolver.updateNumberType(expressionType, expression, context);
JetType expressionType;
if (value instanceof IntegerValueTypeConstant) {
IntegerValueTypeConstant integerValueTypeConstant = (IntegerValueTypeConstant) value;
if (context.contextDependency == INDEPENDENT) {
expressionType = integerValueTypeConstant.getType(context.expectedType);
ArgumentTypeResolver.updateNumberType(expressionType, expression, context);
}
else {
expressionType = integerValueTypeConstant.getUnknownIntegerType();
}
}
else {
expressionType = ((TypedCompileTimeConstant<?>) value).getType();
}
return TypeInfoFactoryPackage.createCheckedTypeInfo(expressionType, context, expression);