Move IS_PURE for constant inside CompileTimeConstant

This commit is contained in:
Natalia Ukhorskaya
2014-01-24 14:01:48 +04:00
parent 3f429116e5
commit 36810c5bbb
30 changed files with 127 additions and 139 deletions
@@ -94,18 +94,13 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
fun isLongWithSuffix() = expression.getNode().getElementType() == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createCompileTimeConstant(result, expression, expectedType, !isLongWithSuffix())
return createCompileTimeConstant(result, expectedType, !isLongWithSuffix())
}
override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? {
val deparenthesizedExpression = JetPsiUtil.deparenthesize(expression)
if (deparenthesizedExpression != null && deparenthesizedExpression != expression) {
val compileTimeConstant = evaluate(deparenthesizedExpression, expectedType)
val isDeparentesizedPure = trace.get(BindingContext.IS_PURE_CONSTANT_EXPRESSION, deparenthesizedExpression)
if (isDeparentesizedPure != null && isDeparentesizedPure!!) {
trace.record(BindingContext.IS_PURE_CONSTANT_EXPRESSION, expression, true)
}
return compileTimeConstant
return evaluate(deparenthesizedExpression, expectedType)
}
return null
}
@@ -135,7 +130,10 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
sb.append(constant.getValue())
}
}
return if (!interupted) createCompileTimeConstant(sb.toString(), expression, expectedType, true, canBeUsedInAnnotation) else null
return if (!interupted)
createCompileTimeConstant(sb.toString(), expectedType,
isPure = true, canBeUsedInAnnotation = canBeUsedInAnnotation)
else null
}
override fun visitBinaryExpression(expression: JetBinaryExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -163,7 +161,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
JetTokens.OROR -> leftValue as Boolean || rightValue as Boolean
else -> throw IllegalArgumentException("Unknown boolean operation token ${operationToken}")
}
return createCompileTimeConstant(result, expression, expectedType)
return createCompileTimeConstant(result, expectedType)
}
else {
return evaluateCall(expression, expression.getOperationReference(), leftExpression, expectedType)
@@ -183,10 +181,10 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val argumentsEntrySet = resolvedCall.getValueArguments().entrySet()
if (argumentsEntrySet.isEmpty()) {
val result = evaluateUnaryAndCheck(argumentForReceiver, resultingDescriptorName.asString(), callExpression)
val isArgumentPure = trace.get(BindingContext.IS_PURE_CONSTANT_EXPRESSION, argumentForReceiver.expression) ?: false
val isArgumentPure = isPureConstant(argumentForReceiver.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression)
val isNumberConversionMethod = resultingDescriptorName in OperatorConventions.NUMBER_CONVERSIONS
return createCompileTimeConstant(result, fullExpression, expectedType, !isNumberConversionMethod && isArgumentPure, canBeUsedInAnnotation)
return createCompileTimeConstant(result, expectedType, !isNumberConversionMethod && isArgumentPure, canBeUsedInAnnotation)
}
else if (argumentsEntrySet.size() == 1) {
val (parameter, argument) = argumentsEntrySet.first()
@@ -200,16 +198,14 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val result = evaluateBinaryAndCheck(argumentForReceiver, argumentForParameter, resultingDescriptorName.asString(), callExpression)
val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression)
val context = EvaluatorContext(canBeUsedInAnnotation)
val c = EvaluatorContext(canBeUsedInAnnotation, areArgumentsPure)
return when(resultingDescriptorName) {
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, context)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, context)
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, c)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, c)
else -> {
val areArgumentsPure = trace.get(BindingContext.IS_PURE_CONSTANT_EXPRESSION, argumentForReceiver.expression) ?: false &&
trace.get(BindingContext.IS_PURE_CONSTANT_EXPRESSION, argumentForParameter.expression) ?: false
createCompileTimeConstant(result, fullExpression, expectedType, areArgumentsPure, canBeUsedInAnnotation)
createCompileTimeConstant(result, expectedType, areArgumentsPure, canBeUsedInAnnotation)
}
}
}
@@ -218,6 +214,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
}
private fun canBeUsedInAnnotation(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.canBeUsedInAnnotations() ?: false
private fun isPureConstant(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.isPure() ?: false
private fun evaluateUnaryAndCheck(receiver: OperationArgument, name: String, callExpression: JetExpression): Any? {
val functions = unaryOperations[UnaryOperationKey(receiver.ctcType, name)]
@@ -299,8 +296,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
compileTimeConstant.getValue(expectedType ?: TypeUtils.NO_EXPECTED_TYPE)
else
compileTimeConstant.getValue()
return createCompileTimeConstant(value, expression, expectedType, false,
AnnotationUtils.isPropertyCompileTimeConstant(callableDescriptor))
return createCompileTimeConstant(value, expectedType, isPure = false,
canBeUsedInAnnotation = AnnotationUtils.isPropertyCompileTimeConstant(callableDescriptor))
}
}
return null
@@ -321,11 +318,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
// MyEnum.A, Integer.MAX_VALUE
if (selectorExpression != null) {
val compileTimeConstant = evaluate(selectorExpression, expectedType)
if (trace.get(BindingContext.IS_PURE_CONSTANT_EXPRESSION, selectorExpression) == true) {
trace.record(BindingContext.IS_PURE_CONSTANT_EXPRESSION, expression, true)
}
return compileTimeConstant
return evaluate(selectorExpression, expectedType)
}
return null
@@ -423,16 +416,9 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
return OperationArgument(evaluationResult, compileTimeType, expression)
}
fun createCompileTimeConstant(value: Any?, expression: JetExpression, expectedType: JetType?, isPure: Boolean = true, canBeUsedInAnnotation: Boolean = true): CompileTimeConstant<*>? {
val c = EvaluatorContext(canBeUsedInAnnotation)
val compileTimeConstant =
if (isPure) {
trace.record(BindingContext.IS_PURE_CONSTANT_EXPRESSION, expression, true)
createCompileTimeConstant(value, c, expectedType ?: TypeUtils.NO_EXPECTED_TYPE)
}
else createCompileTimeConstant(value, c)
return compileTimeConstant
fun createCompileTimeConstant(value: Any?, expectedType: JetType?, isPure: Boolean = true, canBeUsedInAnnotation: Boolean = true): CompileTimeConstant<*>? {
val c = EvaluatorContext(canBeUsedInAnnotation, isPure)
return createCompileTimeConstant(value, c, if (isPure) expectedType ?: TypeUtils.NO_EXPECTED_TYPE else null)
}
}
@@ -453,7 +439,7 @@ public fun recordCompileTimeValueForInitializerIfNeeded(
}
public fun IntegerValueTypeConstant.createCompileTimeConstantWithType(expectedType: JetType): CompileTimeConstant<*>?
= createCompileTimeConstant(getValue(expectedType), EvaluatorContext(canBeUsedInAnnotations()))
= createCompileTimeConstant(this.getValue(expectedType), EvaluatorContext(this.canBeUsedInAnnotations(), this.isPure()))
private fun hasLongSuffix(text: String) = text.endsWith('l') || text.endsWith('L')
@@ -510,16 +496,16 @@ private fun parseBoolean(text: String): Boolean {
}
private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, context: EvaluatorContext): CompileTimeConstant<*>? {
private fun createCompileTimeConstantForEquals(result: Any?, operationReference: JetExpression, c: EvaluatorContext): CompileTimeConstant<*>? {
if (result is Boolean) {
assert(operationReference is JetSimpleNameExpression, "This method should be called only for equals operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
return when (operationToken) {
JetTokens.EQEQ -> BooleanValue(result, context.canBeUsedInAnnotation)
JetTokens.EXCLEQ -> BooleanValue(!result, context.canBeUsedInAnnotation)
JetTokens.EQEQ -> BooleanValue(result, c.canBeUsedInAnnotation)
JetTokens.EXCLEQ -> BooleanValue(!result, c.canBeUsedInAnnotation)
JetTokens.IDENTIFIER -> {
assert ((operationReference as JetSimpleNameExpression).getReferencedNameAsName() == OperatorConventions.EQUALS, "This method should be called only for equals operations")
return BooleanValue(result, context.canBeUsedInAnnotation)
return BooleanValue(result, c.canBeUsedInAnnotation)
}
else -> throw IllegalStateException("Unknown equals operation token: $operationToken ${operationReference.getText()}")
}
@@ -527,18 +513,18 @@ private fun createCompileTimeConstantForEquals(result: Any?, operationReference:
return null
}
private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, context: EvaluatorContext): CompileTimeConstant<*>? {
private fun createCompileTimeConstantForCompareTo(result: Any?, operationReference: JetExpression, c: EvaluatorContext): CompileTimeConstant<*>? {
if (result is Int) {
assert(operationReference is JetSimpleNameExpression, "This method should be called only for compareTo operations")
val operationToken = (operationReference as JetSimpleNameExpression).getReferencedNameElementType()
return when (operationToken) {
JetTokens.LT -> BooleanValue(result < 0, context.canBeUsedInAnnotation)
JetTokens.LTEQ -> BooleanValue(result <= 0, context.canBeUsedInAnnotation)
JetTokens.GT -> BooleanValue(result > 0, context.canBeUsedInAnnotation)
JetTokens.GTEQ -> BooleanValue(result >= 0, context.canBeUsedInAnnotation)
JetTokens.LT -> BooleanValue(result < 0, c.canBeUsedInAnnotation)
JetTokens.LTEQ -> BooleanValue(result <= 0, c.canBeUsedInAnnotation)
JetTokens.GT -> BooleanValue(result > 0, c.canBeUsedInAnnotation)
JetTokens.GTEQ -> BooleanValue(result >= 0, c.canBeUsedInAnnotation)
JetTokens.IDENTIFIER -> {
assert ((operationReference as JetSimpleNameExpression).getReferencedNameAsName() == OperatorConventions.COMPARE_TO, "This method should be called only for compareTo operations")
return IntValue(result, context.canBeUsedInAnnotation)
return IntValue(result, c.canBeUsedInAnnotation, c.isPure)
}
else -> throw IllegalStateException("Unknown compareTo operation token: $operationToken")
}
@@ -562,15 +548,15 @@ private fun createStringConstant(value: CompileTimeConstant<*>?): StringValue? {
private fun createCompileTimeConstant(value: Any?, c: EvaluatorContext, expectedType: JetType? = null): CompileTimeConstant<*>? {
if (expectedType == null) {
when(value) {
is Byte -> return ByteValue(value, c.canBeUsedInAnnotation)
is Short -> return ShortValue(value, c.canBeUsedInAnnotation)
is Int -> return IntValue(value, c.canBeUsedInAnnotation)
is Long -> return LongValue(value, c.canBeUsedInAnnotation)
is Byte -> return ByteValue(value, c.canBeUsedInAnnotation, c.isPure)
is Short -> return ShortValue(value, c.canBeUsedInAnnotation, c.isPure)
is Int -> return IntValue(value, c.canBeUsedInAnnotation, c.isPure)
is Long -> return LongValue(value, c.canBeUsedInAnnotation, c.isPure)
}
}
return when(value) {
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), c, expectedType)
is Char -> CharValue(value, c.canBeUsedInAnnotation)
is Char -> CharValue(value, c.canBeUsedInAnnotation, c.isPure)
is Float -> FloatValue(value, c.canBeUsedInAnnotation)
is Double -> DoubleValue(value, c.canBeUsedInAnnotation)
is Boolean -> BooleanValue(value, c.canBeUsedInAnnotation)
@@ -584,8 +570,8 @@ fun isIntegerType(value: Any?) = value is Byte || value is Short || value is Int
private fun getIntegerValue(value: Long, c: EvaluatorContext, expectedType: JetType): CompileTimeConstant<*>? {
fun defaultIntegerValue(value: Long) = when (value) {
value.toInt().toLong() -> IntValue(value.toInt(), c.canBeUsedInAnnotation)
else -> LongValue(value, c.canBeUsedInAnnotation)
value.toInt().toLong() -> IntValue(value.toInt(), c.canBeUsedInAnnotation, c.isPure)
else -> LongValue(value, c.canBeUsedInAnnotation, c.isPure)
}
if (CompileTimeConstantChecker.noExpectedTypeOrError(expectedType)) {
@@ -595,16 +581,16 @@ private fun getIntegerValue(value: Long, c: EvaluatorContext, expectedType: JetT
val builtIns = KotlinBuiltIns.getInstance()
return when (TypeUtils.makeNotNullable(expectedType)) {
builtIns.getLongType() -> LongValue(value, c.canBeUsedInAnnotation)
builtIns.getLongType() -> LongValue(value, c.canBeUsedInAnnotation, c.isPure)
builtIns.getShortType() -> when (value) {
value.toShort().toLong() -> ShortValue(value.toShort(), c.canBeUsedInAnnotation)
value.toShort().toLong() -> ShortValue(value.toShort(), c.canBeUsedInAnnotation, c.isPure)
else -> defaultIntegerValue(value)
}
builtIns.getByteType() -> when (value) {
value.toByte().toLong() -> ByteValue(value.toByte(), c.canBeUsedInAnnotation)
value.toByte().toLong() -> ByteValue(value.toByte(), c.canBeUsedInAnnotation, c.isPure)
else -> defaultIntegerValue(value)
}
builtIns.getCharType() -> IntValue(value.toInt(), c.canBeUsedInAnnotation)
builtIns.getCharType() -> IntValue(value.toInt(), c.canBeUsedInAnnotation, c.isPure)
else -> defaultIntegerValue(value)
}
}
@@ -636,7 +622,7 @@ private fun getCompileTimeType(c: JetType): CompileTimeType<out Any>? {
}
}
private class EvaluatorContext(val canBeUsedInAnnotation: Boolean)
private class EvaluatorContext(val canBeUsedInAnnotation: Boolean, val isPure: Boolean)
private class CompileTimeType<T>
@@ -75,7 +75,6 @@ public interface BindingContext {
WritableSlice<JetAnnotationEntry, AnnotationDescriptorImpl> ANNOTATION =
Slices.<JetAnnotationEntry, AnnotationDescriptorImpl>sliceBuilder().setOpposite(ANNOTATION_DESCRIPTOR_TO_PSI_ELEMENT).build();
WritableSlice<JetExpression, Boolean> IS_PURE_CONSTANT_EXPRESSION = Slices.createSimpleSlice();
WritableSlice<JetExpression, CompileTimeConstant<?>> COMPILE_TIME_VALUE = Slices.createSimpleSlice();
WritableSlice<VariableDescriptor, CompileTimeConstant<?>> COMPILE_TIME_INITIALIZER = Slices.createSimpleSlice();
@@ -466,11 +466,9 @@ public class CallExpressionResolver {
}
CompileTimeConstant<?> value = ConstantExpressionEvaluator.object$.evaluate(expression, context.trace, context.expectedType);
if (Boolean.TRUE.equals(context.trace.get(BindingContext.IS_PURE_CONSTANT_EXPRESSION, expression))) {
if (value != null) {
if (value != null && value.isPure()) {
return BasicExpressionTypingVisitor.createCompileTimeConstantTypeInfo(value, expression, context);
}
}
JetTypeInfo typeInfo = JetTypeInfo.create(selectorReturnType, selectorReturnTypeInfo.getDataFlowInfo());
if (context.contextDependency == INDEPENDENT) {
@@ -26,7 +26,6 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages;
import org.jetbrains.jet.lang.psi.JetConstantExpression;
import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeUtils;
@@ -167,7 +166,7 @@ public class CompileTimeConstantChecker {
if (text.charAt(0) != '\\') {
// No escape
if (text.length() == 1) {
return new CharValue(text.charAt(0), true);
return new CharValue(text.charAt(0), true, true);
}
return createErrorValue(TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL.on(expression, expression));
}
@@ -190,13 +189,13 @@ public class CompileTimeConstantChecker {
if (escaped == null) {
return illegalEscape(expression);
}
return new CharValue(escaped, true);
return new CharValue(escaped, true, true);
case 5:
// unicode escape
if (escape.charAt(0) == 'u') {
try {
Integer intValue = Integer.valueOf(escape.substring(1), 16);
return new CharValue((char) intValue.intValue(), true);
return new CharValue((char) intValue.intValue(), true, true);
} catch (NumberFormatException e) {
// Will be reported below
}
+2 -2
View File
@@ -2,8 +2,8 @@ package test
enum class MyEnum { A }
// val prop1: null
// val prop1: false
val prop1 = MyEnum.A
// val prop2: null
// val prop2: false
val prop2 = javaClass<MyEnum>()
@@ -1,13 +1,13 @@
package test
// val prop1: null
// val prop1: false
val prop1 = 1.toLong() + 1
// val prop2: null
// val prop2: false
val prop2 = -1.toInt()
// val prop3: null
// val prop3: false
val prop3 = 1 + 1.toByte()
// val prop4: null
// val prop4: false
val prop4 = 1 + 1.toShort() + 1
@@ -2,8 +2,8 @@ package test
val NAMED_CONSTANT = 1
// val prop1: null
// val prop1: false
val prop1 = NAMED_CONSTANT
// val prop2: null
// val prop2: false
val prop2 = NAMED_CONSTANT + 1
+5 -5
View File
@@ -1,16 +1,16 @@
package test
// val prop1: true
// val prop1: false
val prop1 = ""
// val prop2: true
// val prop2: false
val prop2 = "a"
// val prop3: true
// val prop3: false
val prop3 = "\"a\""
// val prop5: true
// val prop5: false
val prop5 = "a${1 + 1}"
// val prop6: true
// val prop6: false
val prop6 = "a" + "b"
+7 -7
View File
@@ -1,22 +1,22 @@
package test
// val prop1: null
// val prop1: false
val prop1 = 1.toLong()
// val prop2: null
// val prop2: false
val prop2 = 1.toInt()
// val prop3: null
// val prop3: false
val prop3 = 1.toByte()
// val prop4: null
// val prop4: false
val prop4 = 1.toShort()
// val prop5: null
// val prop5: false
val prop5 = 1.toChar()
// val prop6: null
// val prop6: false
val prop6 = 1.toDouble()
// val prop7: null
// val prop7: false
val prop7 = 1.toFloat()
@@ -3,14 +3,14 @@ package test
// val p1: true
val p1 = -1
// val p2: null
// val p2: false
val p2 = -1.toLong()
// val p3: null
// val p3: false
val p3 = -1.toByte()
// val p4: null
// val p4: false
val p4 = -1.toInt()
// val p5: null
// val p5: false
val p5 = -1.toShort()
@@ -12,52 +12,52 @@ val p3: Byte = -1
// val p4: true
val p4: Short = -1
// val l1: null
// val l1: false
val l1: Long = -1.toLong()
// val l2: null
// val l2: false
val l2: Byte = -1.toLong()
// val l3: null
// val l3: false
val l3: Int = -1.toLong()
// val l4: null
// val l4: false
val l4: Short = -1.toLong()
// val b1: null
// val b1: false
val b1: Byte = -1.toByte()
// val b2: null
// val b2: false
val b2: Int = -1.toByte()
// val b3: null
// val b3: false
val b3: Long = -1.toByte()
// val b4: null
// val b4: false
val b4: Short = -1.toByte()
// val i1: null
// val i1: false
val i1: Byte = -1.toInt()
// val i2: null
// val i2: false
val i2: Int = -1.toInt()
// val i3: null
// val i3: false
val i3: Long = -1.toInt()
// val i4: null
// val i4: false
val i4: Short = -1.toInt()
// val s1: null
// val s1: false
val s1: Byte = -1.toShort()
// val s2: null
// val s2: false
val s2: Int = -1.toShort()
// val s3: null
// val s3: false
val s3: Long = -1.toShort()
// val s4: null
// val s4: false
val s4: Short = -1.toShort()
@@ -54,8 +54,8 @@ abstract class AbstractEvaluateExpressionTest: AbstractAnnotationDescriptorResol
fun doIsPureTest(path: String) {
doTest(path) {
property, context ->
val isPureKey = context.get(BindingContext.IS_PURE_CONSTANT_EXPRESSION, property.getInitializer())
isPureKey.toString()
val compileTimeConstant = context.get(BindingContext.COMPILE_TIME_VALUE, property.getInitializer())
compileTimeConstant?.isPure().toString()
}
}