Refactoring of CompileTimeConstant, introduce flag to represent a constant being referenced by a variable

This commit is contained in:
Tal Man
2014-04-09 14:38:50 -04:00
parent 32a2e63ae5
commit aeb5bae556
38 changed files with 303 additions and 130 deletions
@@ -38,6 +38,7 @@ public class JavaPropertyInitializerEvaluatorImpl implements JavaPropertyInitial
evaluatedExpression,
DescriptorUtils.isPropertyCompileTimeConstant(descriptor),
false,
true,
descriptor.getType());
}
return null;
@@ -54,7 +54,6 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, compileTimeConstant)
return compileTimeConstant
}
return null
}
@@ -70,9 +69,9 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
return createStringConstant(this@ConstantExpressionEvaluator.evaluate(expression, KotlinBuiltIns.getInstance().getStringType()))
}
override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = StringValue(entry.getText(), true)
override fun visitLiteralStringTemplateEntry(entry: JetLiteralStringTemplateEntry, data: Nothing?) = StringValue(entry.getText(), true, false)
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = StringValue(entry.getUnescapedValue(), true)
override fun visitEscapeStringTemplateEntry(entry: JetEscapeStringTemplateEntry, data: Nothing?) = StringValue(entry.getUnescapedValue(), true, false)
}
override fun visitConstantExpression(expression: JetConstantExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -92,8 +91,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
if (result == null) return null
fun isLongWithSuffix() = nodeElementType == JetNodeTypes.INTEGER_CONSTANT && hasLongSuffix(text)
return createCompileTimeConstant(result, expectedType, !isLongWithSuffix())
return createCompileTimeConstant(result, expectedType, !isLongWithSuffix(), true, false)
}
override fun visitParenthesizedExpression(expression: JetParenthesizedExpression, expectedType: JetType?): CompileTimeConstant<*>? {
@@ -118,6 +116,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val sb = StringBuilder()
var interupted = false
var canBeUsedInAnnotation = true
var usesVariableAsConstant = false
for (entry in expression.getEntries()) {
val constant = stringExpressionEvaluator.evaluate(entry)
if (constant == null) {
@@ -126,12 +125,14 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
}
else {
if (!constant.canBeUsedInAnnotations()) canBeUsedInAnnotation = false
if (constant.usesVariableAsConstant()) usesVariableAsConstant = true
sb.append(constant.getValue())
}
}
return if (!interupted)
createCompileTimeConstant(sb.toString(), expectedType,
isPure = true, canBeUsedInAnnotation = canBeUsedInAnnotation)
isPure = true, canBeUsedInAnnotation = canBeUsedInAnnotation,
usesVariableAsConstant = usesVariableAsConstant)
else null
}
@@ -160,7 +161,8 @@ 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, expectedType)
val usesVariableAsConstant = leftConstant.usesVariableAsConstant() || rightConstant.usesVariableAsConstant()
return createCompileTimeConstant(result, expectedType, true, true, usesVariableAsConstant)
}
else {
return evaluateCall(expression.getOperationReference(), leftExpression, expectedType)
@@ -182,12 +184,16 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
if (result == null) return null
val isArgumentPure = isPureConstant(argumentForReceiver.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression)
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression)
val isNumberConversionMethod = resultingDescriptorName in OperatorConventions.NUMBER_CONVERSIONS
return createCompileTimeConstant(result, expectedType, !isNumberConversionMethod && isArgumentPure, canBeUsedInAnnotation)
return createCompileTimeConstant(result,
expectedType,
!isNumberConversionMethod && isArgumentPure,
canBeUsedInAnnotation,
usesVariableAsConstant)
}
else if (argumentsEntrySet.size() == 1) {
val (parameter, argument) = argumentsEntrySet.first()
val argumentForParameter = createOperationArgumentForFirstParameter(argument, parameter)
if (argumentForParameter == null) return null
@@ -200,12 +206,13 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val areArgumentsPure = isPureConstant(argumentForReceiver.expression) && isPureConstant(argumentForParameter.expression)
val canBeUsedInAnnotation = canBeUsedInAnnotation(argumentForReceiver.expression) && canBeUsedInAnnotation(argumentForParameter.expression)
val c = EvaluatorContext(canBeUsedInAnnotation, areArgumentsPure)
val usesVariableAsConstant = usesVariableAsConstant(argumentForReceiver.expression) || usesVariableAsConstant(argumentForParameter.expression)
val c = EvaluatorContext(canBeUsedInAnnotation, areArgumentsPure, usesVariableAsConstant)
return when(resultingDescriptorName) {
OperatorConventions.COMPARE_TO -> createCompileTimeConstantForCompareTo(result, callExpression, c)
OperatorConventions.EQUALS -> createCompileTimeConstantForEquals(result, callExpression, c)
else -> {
createCompileTimeConstant(result, expectedType, areArgumentsPure, canBeUsedInAnnotation)
createCompileTimeConstant(result, expectedType, areArgumentsPure, canBeUsedInAnnotation, usesVariableAsConstant)
}
}
}
@@ -213,7 +220,10 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
return null
}
private fun usesVariableAsConstant(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.usesVariableAsConstant() ?: false
private fun canBeUsedInAnnotation(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.canBeUsedInAnnotations() ?: false
private fun isPureConstant(expression: JetExpression): Boolean {
val compileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)
if (compileTimeConstant is IntegerValueConstant) {
@@ -287,7 +297,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
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 EnumValue(enumDescriptor as ClassDescriptor, false);
}
val resolvedCall = trace.getBindingContext().get(BindingContext.RESOLVED_CALL, expression)
@@ -303,7 +313,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
else
compileTimeConstant.getValue()
return createCompileTimeConstant(value, expectedType, isPure = false,
canBeUsedInAnnotation = DescriptorUtils.isPropertyCompileTimeConstant(callableDescriptor))
canBeUsedInAnnotation = DescriptorUtils.isPropertyCompileTimeConstant(callableDescriptor),
usesVariableAsConstant = true)
}
}
return null
@@ -341,7 +352,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val varargType = resultingDescriptor.getValueParameters().first?.getVarargElementType()!!
val arguments = call.getValueArguments().values().flatMap { resolveArguments(it.getArguments(), varargType) }
return ArrayValue(arguments, resultingDescriptor.getReturnType()!!, true)
return ArrayValue(arguments, resultingDescriptor.getReturnType()!!, true, arguments.any() { it.usesVariableAsConstant() })
}
// Ann()
@@ -402,7 +413,6 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
val argumentExpression = arguments.first().getArgumentExpression()
if (argumentExpression == null) return null
return createOperationArgument(argumentExpression, parameter.getType(), argumentCompileTimeType)
}
@@ -421,8 +431,12 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
return OperationArgument(evaluationResult, compileTimeType, expression)
}
fun createCompileTimeConstant(value: Any?, expectedType: JetType?, isPure: Boolean = true, canBeUsedInAnnotation: Boolean = true): CompileTimeConstant<*>? {
val c = EvaluatorContext(canBeUsedInAnnotation, isPure)
fun createCompileTimeConstant(value: Any?,
expectedType: JetType?,
isPure: Boolean = true,
canBeUsedInAnnotation: Boolean = true,
usesVariableAsConstant: Boolean = false): CompileTimeConstant<*>? {
val c = EvaluatorContext(canBeUsedInAnnotation, isPure, usesVariableAsConstant)
return createCompileTimeConstant(value, c, if (isPure) expectedType ?: TypeUtils.NO_EXPECTED_TYPE else null)
}
}
@@ -490,11 +504,11 @@ private fun createCompileTimeConstantForEquals(result: Any?, operationReference:
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, c.canBeUsedInAnnotation)
JetTokens.EXCLEQ -> BooleanValue(!result, c.canBeUsedInAnnotation)
JetTokens.EQEQ -> BooleanValue(result, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.EXCLEQ -> BooleanValue(!result, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.IDENTIFIER -> {
assert ((operationReference as JetSimpleNameExpression).getReferencedNameAsName() == OperatorConventions.EQUALS, "This method should be called only for equals operations")
return BooleanValue(result, c.canBeUsedInAnnotation)
return BooleanValue(result, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
}
else -> throw IllegalStateException("Unknown equals operation token: $operationToken ${operationReference.getText()}")
}
@@ -507,13 +521,13 @@ private fun createCompileTimeConstantForCompareTo(result: Any?, operationReferen
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, c.canBeUsedInAnnotation)
JetTokens.LTEQ -> BooleanValue(result <= 0, c.canBeUsedInAnnotation)
JetTokens.GT -> BooleanValue(result > 0, c.canBeUsedInAnnotation)
JetTokens.GTEQ -> BooleanValue(result >= 0, c.canBeUsedInAnnotation)
JetTokens.LT -> BooleanValue(result < 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.LTEQ -> BooleanValue(result <= 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.GT -> BooleanValue(result > 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.GTEQ -> BooleanValue(result >= 0, c.canBeUsedInAnnotation, c.usesVariableAsConstant)
JetTokens.IDENTIFIER -> {
assert ((operationReference as JetSimpleNameExpression).getReferencedNameAsName() == OperatorConventions.COMPARE_TO, "This method should be called only for compareTo operations")
return IntValue(result, c.canBeUsedInAnnotation, c.isPure)
return IntValue(result, c.canBeUsedInAnnotation, c.isPure, c.usesVariableAsConstant)
}
else -> throw IllegalStateException("Unknown compareTo operation token: $operationToken")
}
@@ -523,19 +537,19 @@ private fun createCompileTimeConstantForCompareTo(result: Any?, operationReferen
private fun createStringConstant(value: CompileTimeConstant<*>?): StringValue? {
return when (value) {
is IntegerValueTypeConstant -> StringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString(), value.canBeUsedInAnnotations())
is IntegerValueTypeConstant -> StringValue(value.getValue(TypeUtils.NO_EXPECTED_TYPE).toString(), value.canBeUsedInAnnotations(), value.usesVariableAsConstant())
is StringValue -> value
is IntValue, is ByteValue, is ShortValue, is LongValue,
is CharValue,
is DoubleValue, is FloatValue,
is BooleanValue,
is NullValue -> StringValue(value.getValue().toString(), value.canBeUsedInAnnotations())
is NullValue -> StringValue(value.getValue().toString(), value.canBeUsedInAnnotations(), value.usesVariableAsConstant())
else -> null
}
}
private fun createCompileTimeConstant(value: Any?, c: EvaluatorContext, expectedType: JetType? = null): CompileTimeConstant<*>? {
return createCompileTimeConstant(value, c.canBeUsedInAnnotation, c.isPure, expectedType)
return createCompileTimeConstant(value, c.canBeUsedInAnnotation, c.isPure, c.usesVariableAsConstant, expectedType)
}
fun isIntegerType(value: Any?) = value is Byte || value is Short || value is Int || value is Long
@@ -567,7 +581,7 @@ private fun getCompileTimeType(c: JetType): CompileTimeType<out Any>? {
}
}
private class EvaluatorContext(val canBeUsedInAnnotation: Boolean, val isPure: Boolean)
private class EvaluatorContext(val canBeUsedInAnnotation: Boolean, val isPure: Boolean, val usesVariableAsConstant: Boolean = false)
private class CompileTimeType<T>
@@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -282,11 +283,20 @@ public class AnnotationResolver {
trace);
if (argumentsAsVararg) {
boolean usesVariableAsConstant = KotlinPackage.any(constants, new Function1<CompileTimeConstant<?>, Boolean>() {
@Override
public Boolean invoke(CompileTimeConstant<?> constant) {
return constant.usesVariableAsConstant();
}
});
JetType arrayType = KotlinBuiltIns.getInstance().getPrimitiveArrayJetTypeByPrimitiveJetType(varargElementType);
if (arrayType == null) {
arrayType = KotlinBuiltIns.getInstance().getArrayType(varargElementType);
}
return new ArrayValue(constants, arrayType, true);
return new ArrayValue(constants, arrayType, true, usesVariableAsConstant);
}
else {
// we should actually get only one element, but just in case of getting many, we take the last one
@@ -18,13 +18,11 @@ package org.jetbrains.jet.lang.resolve;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.JetNodeTypes;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.descriptors.VariableDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.Annotated;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.jet.lang.psi.JetConstantExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetParameter;
import org.jetbrains.jet.lang.psi.JetTypeReference;
@@ -129,12 +127,10 @@ public class CompileTimeConstantUtils {
@NotNull BindingTrace trace,
@Nullable Boolean expectedValue
) {
if (!(expression instanceof JetConstantExpression) || expression.getNode().getElementType() != JetNodeTypes.BOOLEAN_CONSTANT) {
return false;
}
if (expression == null) return false;
CompileTimeConstant<?> compileTimeConstant =
ConstantExpressionEvaluator.object$.evaluate(expression, trace, KotlinBuiltIns.getInstance().getBooleanType());
if (!(compileTimeConstant instanceof BooleanValue)) return false;
if (!(compileTimeConstant instanceof BooleanValue) || compileTimeConstant.usesVariableAsConstant()) return false;
Boolean value = ((BooleanValue) compileTimeConstant).getValue();
return expectedValue == null || expectedValue.equals(value);
@@ -1,25 +1,25 @@
== main ==
fun main() {
while(1 > 0) {
while(0 > 1) {
2
}
}
---------------------
L0:
1 <START>
2 mark({ while(1 > 0) { 2 } })
mark(while(1 > 0) { 2 })
2 mark({ while(0 > 1) { 2 } })
mark(while(0 > 1) { 2 })
L2 [loop entry point]:
L5 [condition entry point]:
mark(1 > 0) PREV:[mark(while(1 > 0) { 2 }), jmp(L2 [loop entry point])]
r(1)
mark(0 > 1) PREV:[mark(while(0 > 1) { 2 }), jmp(L2 [loop entry point])]
r(0)
r(1)
call(>, compareTo)
jf(L3 [loop exit point]) NEXT:[read (Unit), mark({ 2 })]
L4 [body entry point]:
3 mark({ 2 })
r(2)
2 jmp(L2 [loop entry point]) NEXT:[mark(1 > 0)]
2 jmp(L2 [loop entry point]) NEXT:[mark(0 > 1)]
L3 [loop exit point]:
read (Unit) PREV:[jf(L3 [loop exit point])]
L1:
@@ -32,21 +32,21 @@ sink:
== dowhile ==
fun dowhile() {
do {return}
while(1 > 0)
while(0 > 1)
}
---------------------
L0:
1 <START>
2 mark({ do {return} while(1 > 0) })
3 mark(do {return} while(1 > 0))
2 mark({ do {return} while(0 > 1) })
3 mark(do {return} while(0 > 1))
L2 [loop entry point]:
L4 [body entry point]:
mark({return})
ret L1 NEXT:[<END>]
L5 [condition entry point]:
- mark(1 > 0) PREV:[]
- r(1) PREV:[]
- mark(0 > 1) PREV:[]
- r(0) PREV:[]
- r(1) PREV:[]
- call(>, compareTo) PREV:[]
- jt(L2 [loop entry point]) NEXT:[read (Unit), mark({return})] PREV:[]
L3 [loop exit point]:
@@ -1,10 +1,10 @@
fun main() {
while(1 > 0) {
while(0 > 1) {
2
}
}
fun dowhile() {
do {return}
while(1 > 0)
while(0 > 1)
}
@@ -1,9 +1,9 @@
fun test() {
[ann]
while (1 < 2) {}
while (2 < 1) {}
[ann]
do {} while (1 < 2)
do {} while (2 < 1)
[ann]
for (i in 1..2) {}
@@ -0,0 +1,12 @@
fun test() {
[ann]
while (2 > 1) {}
[ann]
<!UNREACHABLE_CODE!>do {} while (2 > 1)<!>
[ann]
<!UNREACHABLE_CODE!>for (i in 1..2) {}<!>
}
annotation class ann
@@ -0,0 +1,11 @@
package test
val x = 1
val y = "a"
// val prop1: true
val prop1 = x
// val prop2: true
val prop2 = y
@@ -0,0 +1,16 @@
package test
enum class MyEnum { A;B }
fun foo(): Boolean = true
val x = 1
// val prop1: null
val prop1 = MyEnum.A
// val prop2: null
val prop2 = foo()
// val prop3: true
val prop3 = "$x"
@@ -0,0 +1,29 @@
package test
val x = 1
val y = true
// val prop1: false
val prop1 = 1 > 2
// val prop2: false
val prop2 = 2 + 3
// val prop3: true
val prop3 = 2 + x
// val prop4: true
val prop4 = x < 2
// val prop5: true
val prop5 = y && true
// val prop6: false
val prop6 = true && false || 2 > 1
// val prop7: true
val prop7 = x == 1
// val prop8: true
val prop8 = 1 / x
@@ -0,0 +1,23 @@
package test
// val prop1: false
val prop1 = 1
// val prop2: false
val prop2 = "hello"
// val prop3: false
val prop3 = true
// val prop4: false
val prop4 = false
// val prop5: false
val prop5 = -7453
// val prop6: false
val prop6 = 3.56
// val prop7: false
val prop7 = 5464564L
@@ -616,6 +616,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/annotations/onLoops.kt");
}
@TestMetadata("onLoopsUnreachable.kt")
public void testOnLoopsUnreachable() throws Exception {
doTest("compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.kt");
}
@TestMetadata("onMultiDeclaration.kt")
public void testOnMultiDeclaration() throws Exception {
doTest("compiler/testData/diagnostics/tests/annotations/onMultiDeclaration.kt");
@@ -20,20 +20,18 @@ import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.resolve.annotation.AbstractAnnotationDescriptorResolveTest
import java.io.File
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.jet.lang.resolve.BindingContextUtils
import org.jetbrains.jet.lang.psi.JetProperty
import org.jetbrains.jet.InTextDirectivesUtils
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import java.util.regex.Pattern
import org.intellij.lang.annotations.RegExp
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.jet.JetTestUtils
import org.jetbrains.jet.util.slicedmap.WritableSlice
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant
import org.jetbrains.jet.lang.resolve.constants.StringValue
import org.jetbrains.jet.lang.descriptors.VariableDescriptor
import org.jetbrains.jet.lang.resolve.constants.IntegerValueConstant
import java.util.HashMap
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.evaluate.ConstantExpressionEvaluator
import org.jetbrains.jet.lang.resolve.DelegatingBindingTrace
import org.jetbrains.jet.lang.types.TypeUtils
abstract class AbstractEvaluateExpressionTest : AbstractAnnotationDescriptorResolveTest() {
@@ -63,6 +61,19 @@ abstract class AbstractEvaluateExpressionTest : AbstractAnnotationDescriptorReso
}
}
// Test directives should look like [// val testedPropertyName: expectedValue]
fun doUsesVariableAsConstantTest(path: String) {
doTest(path) {
property, context ->
val compileTimeConstant = property.getCompileTimeInitializer()
if (compileTimeConstant == null) {
"null"
} else {
compileTimeConstant.usesVariableAsConstant().toString()
}
}
}
private fun doTest(path: String, getValueToTest: (VariableDescriptor, BindingContext) -> String) {
val myFile = File(path)
val fileText = FileUtil.loadFile(myFile, true)
@@ -30,7 +30,7 @@ import org.jetbrains.jet.evaluate.AbstractEvaluateExpressionTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@InnerTestClasses({EvaluateExpressionTestGenerated.Constant.class, EvaluateExpressionTestGenerated.IsPure.class})
@InnerTestClasses({EvaluateExpressionTestGenerated.Constant.class, EvaluateExpressionTestGenerated.IsPure.class, EvaluateExpressionTestGenerated.UsesVariableAsConstant.class})
public class EvaluateExpressionTestGenerated extends AbstractEvaluateExpressionTest {
@TestMetadata("compiler/testData/evaluate/constant")
public static class Constant extends AbstractEvaluateExpressionTest {
@@ -168,10 +168,39 @@ public class EvaluateExpressionTestGenerated extends AbstractEvaluateExpressionT
}
@TestMetadata("compiler/testData/evaluate/usesVariableAsConstant")
public static class UsesVariableAsConstant extends AbstractEvaluateExpressionTest {
public void testAllFilesPresentInUsesVariableAsConstant() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/evaluate/usesVariableAsConstant"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("binaryTypes.kt")
public void testBinaryTypes() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/binaryTypes.kt");
}
@TestMetadata("NamedConstants.kt")
public void testNamedConstants() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/NamedConstants.kt");
}
@TestMetadata("OtherTypes.kt")
public void testOtherTypes() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/OtherTypes.kt");
}
@TestMetadata("simpleTypes.kt")
public void testSimpleTypes() throws Exception {
doUsesVariableAsConstantTest("compiler/testData/evaluate/usesVariableAsConstant/simpleTypes.kt");
}
}
public static Test suite() {
TestSuite suite = new TestSuite("EvaluateExpressionTestGenerated");
suite.addTestSuite(Constant.class);
suite.addTestSuite(IsPure.class);
suite.addTestSuite(UsesVariableAsConstant.class);
return suite;
}
}
@@ -108,7 +108,7 @@ class LazyJavaAnnotationDescriptor(
private fun resolveAnnotationArgument(argument: JavaAnnotationArgument?): CompileTimeConstant<*>? {
return when (argument) {
is JavaLiteralAnnotationArgument -> createCompileTimeConstant(argument.getValue(), true, false, null)
is JavaLiteralAnnotationArgument -> createCompileTimeConstant(argument.getValue(), true, false, false, null)
is JavaReferenceAnnotationArgument -> resolveFromReference(argument.resolve())
is JavaArrayAnnotationArgument -> resolveFromArray(argument.getName() ?: DEFAULT_ANNOTATION_MEMBER_NAME, argument.getElements())
is JavaAnnotationAsAnnotationArgument -> resolveFromAnnotation(argument.getAnnotation())
@@ -137,7 +137,7 @@ class LazyJavaAnnotationDescriptor(
val values = elements.map {
argument -> resolveAnnotationArgument(argument) ?: NullValue.NULL
}
return ArrayValue(values, valueParameter.getType(), true)
return ArrayValue(values, valueParameter.getType(), true, values.any { it.usesVariableAsConstant() })
}
private fun resolveFromReference(element: JavaElement?): CompileTimeConstant<*>? {
@@ -154,7 +154,7 @@ class LazyJavaAnnotationDescriptor(
val classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(element.getName())
if (classifier !is ClassDescriptor) return null
return EnumValue(classifier)
return EnumValue(classifier, false)
}
private fun resolveFromJavaClassObjectType(javaType: JavaType): CompileTimeConstant<*>? {
@@ -126,7 +126,7 @@ public class AnnotationDescriptorDeserializer extends BaseDescriptorDeserializer
@Override
public void visit(@Nullable Name name, @Nullable Object value) {
if (name != null) {
CompileTimeConstant<?> argument = ConstantsPackage.createCompileTimeConstant(value, true, false, null);
CompileTimeConstant<?> argument = ConstantsPackage.createCompileTimeConstant(value, true, false, false, null);
setArgumentValueByName(name, argument != null ? argument : ErrorValue.create("Unsupported annotation argument: " + name));
}
}
@@ -149,7 +149,7 @@ public class AnnotationDescriptorDeserializer extends BaseDescriptorDeserializer
if (enumClass.getKind() == ClassKind.ENUM_CLASS) {
ClassifierDescriptor classifier = enumClass.getUnsubstitutedInnerClassesScope().getClassifier(name);
if (classifier instanceof ClassDescriptor) {
return new EnumValue((ClassDescriptor) classifier);
return new EnumValue((ClassDescriptor) classifier, false);
}
}
return ErrorValue.create("Unresolved enum entry: " + enumClassName.getInternalName() + "." + name);
@@ -89,7 +89,7 @@ public class DescriptorDeserializersStorage {
MemberSignature signature = MemberSignature.fromFieldNameAndDesc(name, desc);
if (initializer != null) {
propertyConstants.put(signature, ConstantsPackage.createCompileTimeConstant(
initializer, /* canBeUsedInAnnotation */ true, /* isPureIntConstant */ true, /* expectedType */ null));
initializer, /* canBeUsedInAnnotation */ true, /* isPureIntConstant */ true, /* usesVariableAsConstant */ true, /* expectedType */ null));
}
return new MemberAnnotationVisitor(signature);
}
@@ -25,7 +25,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class AnnotationValue extends CompileTimeConstant<AnnotationDescriptor> {
public AnnotationValue(@NotNull AnnotationDescriptor value) {
super(value, true);
super(value, true, false, false);
}
@NotNull
@@ -27,8 +27,11 @@ public class ArrayValue extends CompileTimeConstant<List<CompileTimeConstant<?>>
private final JetType type;
public ArrayValue(@NotNull List<CompileTimeConstant<?>> value, @NotNull JetType type, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public ArrayValue(@NotNull List<CompileTimeConstant<?>> value,
@NotNull JetType type,
boolean canBeUsedInAnnotations,
boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
this.type = type;
}
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class BooleanValue extends CompileTimeConstant<Boolean> {
public BooleanValue(boolean value, boolean canBeUseInAnnotation) {
super(value, canBeUseInAnnotation);
public BooleanValue(boolean value, boolean canBeUseInAnnotation, boolean usesVariableAsConstant) {
super(value, canBeUseInAnnotation, false, usesVariableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class ByteValue extends IntegerValueConstant<Byte> {
public ByteValue(byte value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public ByteValue(byte value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVaraiableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVaraiableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class CharValue extends IntegerValueConstant<Character> {
public CharValue(char value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public CharValue(char value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -24,15 +24,38 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public abstract class CompileTimeConstant<T> {
protected final T value;
private final boolean canBeUsedInAnnotations;
private final int flags;
protected CompileTimeConstant(T value, boolean canBeUsedInAnnotations) {
/*
* if is pure is false then constant type cannot be changed
* ex1. val a: Long = 1.toInt() (TYPE_MISMATCH error, 1.toInt() isn't pure)
* ex2. val b: Int = a (TYPE_MISMATCH error, a isn't pure)
*
*/
private static final int IS_PURE_MASK = 1;
private static final int CAN_BE_USED_IN_ANNOTATIONS_MASK = 1 << 1;
private static final int USES_VARIABLE_AS_CONSTANT_MASK = 1 << 2;
protected CompileTimeConstant(T value,
boolean canBeUsedInAnnotations,
boolean isPure,
boolean usesVariableAsConstant) {
this.value = value;
this.canBeUsedInAnnotations = canBeUsedInAnnotations;
flags = (isPure ? IS_PURE_MASK : 0) |
(canBeUsedInAnnotations ? CAN_BE_USED_IN_ANNOTATIONS_MASK : 0) |
(usesVariableAsConstant ? USES_VARIABLE_AS_CONSTANT_MASK : 0);
}
public boolean canBeUsedInAnnotations() {
return canBeUsedInAnnotations;
return (flags & CAN_BE_USED_IN_ANNOTATIONS_MASK) != 0;
}
public boolean isPure() {
return (flags & IS_PURE_MASK) != 0;
}
public boolean usesVariableAsConstant() {
return (flags & USES_VARIABLE_AS_CONSTANT_MASK) != 0;
}
@Nullable
@@ -24,23 +24,24 @@ public fun createCompileTimeConstant(
value: Any?,
canBeUsedInAnnotation: Boolean,
isPureIntConstant: Boolean,
usesVariableAsConstant: Boolean = false,
expectedType: JetType? = null
): CompileTimeConstant<*>? {
if (expectedType == null) {
when(value) {
is Byte -> return ByteValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Short -> return ShortValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Int -> return IntValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Long -> return LongValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Byte -> return ByteValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Short -> return ShortValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Int -> return IntValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Long -> return LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
}
}
return when(value) {
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), canBeUsedInAnnotation, isPureIntConstant, expectedType)
is Char -> CharValue(value, canBeUsedInAnnotation, isPureIntConstant)
is Float -> FloatValue(value, canBeUsedInAnnotation)
is Double -> DoubleValue(value, canBeUsedInAnnotation)
is Boolean -> BooleanValue(value, canBeUsedInAnnotation)
is String -> StringValue(value, canBeUsedInAnnotation)
is Byte, is Short, is Int, is Long -> getIntegerValue((value as Number).toLong(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant, expectedType)
is Char -> CharValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
is Float -> FloatValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
is Double -> DoubleValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
is Boolean -> BooleanValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
is String -> StringValue(value, canBeUsedInAnnotation, usesVariableAsConstant)
null -> NullValue.NULL
else -> null
}
@@ -50,30 +51,31 @@ private fun getIntegerValue(
value: Long,
canBeUsedInAnnotation: Boolean,
isPureIntConstant: Boolean,
usesVariableAsConstant: Boolean,
expectedType: JetType
): CompileTimeConstant<*>? {
fun defaultIntegerValue(value: Long) = when (value) {
value.toInt().toLong() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant)
else -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant)
value.toInt().toLong() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
}
if (TypeUtils.noExpectedType(expectedType) || expectedType.isError()) {
return IntegerValueTypeConstant(value, canBeUsedInAnnotation)
return IntegerValueTypeConstant(value, canBeUsedInAnnotation, usesVariableAsConstant)
}
val builtIns = KotlinBuiltIns.getInstance()
return when (TypeUtils.makeNotNullable(expectedType)) {
builtIns.getLongType() -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant)
builtIns.getLongType() -> LongValue(value, canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
builtIns.getShortType() -> when (value) {
value.toShort().toLong() -> ShortValue(value.toShort(), canBeUsedInAnnotation, isPureIntConstant)
value.toShort().toLong() -> ShortValue(value.toShort(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> defaultIntegerValue(value)
}
builtIns.getByteType() -> when (value) {
value.toByte().toLong() -> ByteValue(value.toByte(), canBeUsedInAnnotation, isPureIntConstant)
value.toByte().toLong() -> ByteValue(value.toByte(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> defaultIntegerValue(value)
}
builtIns.getCharType() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant)
builtIns.getCharType() -> IntValue(value.toInt(), canBeUsedInAnnotation, isPureIntConstant, usesVariableAsConstant)
else -> defaultIntegerValue(value)
}
}
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class DoubleValue extends CompileTimeConstant<Double> {
public DoubleValue(double value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public DoubleValue(double value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
}
@NotNull
@@ -24,8 +24,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class EnumValue extends CompileTimeConstant<ClassDescriptor> {
public EnumValue(@NotNull ClassDescriptor value) {
super(value, true);
public EnumValue(@NotNull ClassDescriptor value, boolean usesVariableAsConstant) {
super(value, true, false, usesVariableAsConstant);
}
@NotNull
@@ -25,7 +25,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public abstract class ErrorValue extends CompileTimeConstant<Void> {
public ErrorValue() {
super(null, true);
super(null, true, false, false);
}
@Override
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class FloatValue extends CompileTimeConstant<Float> {
public FloatValue(float value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public FloatValue(float value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class IntValue extends IntegerValueConstant<Integer> {
public IntValue(int value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public IntValue(int value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -18,19 +18,7 @@ package org.jetbrains.jet.lang.resolve.constants;
public abstract class IntegerValueConstant<T> extends CompileTimeConstant<T> {
/*
* if false then constant type cannot be changed
* ex1. val a: Long = 1.toInt() (TYPE_MISMATCH error, 1.toInt() isn't pure)
* ex2. val b: Int = a (TYPE_MISMATCH error, a isn't pure)
* */
private final boolean isPure;
protected IntegerValueConstant(T value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations);
isPure = pure;
}
public boolean isPure() {
return isPure;
protected IntegerValueConstant(T value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVaraiableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVaraiableAsConstant);
}
}
@@ -19,10 +19,9 @@ package org.jetbrains.jet.lang.resolve.constants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationArgumentVisitor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import java.util.Collections;
@@ -30,8 +29,8 @@ public class IntegerValueTypeConstant extends IntegerValueConstant<Number> {
private final IntegerValueTypeConstructor typeConstructor;
public IntegerValueTypeConstant(@NotNull Number value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations, true);
public IntegerValueTypeConstant(@NotNull Number value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, true, usesVariableAsConstant);
this.typeConstructor = new IntegerValueTypeConstructor(value.longValue());
}
@@ -24,7 +24,7 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class JavaClassValue extends CompileTimeConstant<JetType> {
public JavaClassValue(@NotNull JetType value) {
super(value, true);
super(value, true, false, false);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class LongValue extends IntegerValueConstant<Long> {
public LongValue(long value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public LongValue(long value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -26,7 +26,7 @@ public class NullValue extends CompileTimeConstant<Void> {
public static final NullValue NULL = new NullValue();
private NullValue() {
super(null, false);
super(null, false, false, false);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
public class ShortValue extends IntegerValueConstant<Short> {
public ShortValue(short value, boolean canBeUsedInAnnotations, boolean pure) {
super(value, canBeUsedInAnnotations, pure);
public ShortValue(short value, boolean canBeUsedInAnnotations, boolean pure, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, pure, usesVariableAsConstant);
}
@NotNull
@@ -23,8 +23,8 @@ import org.jetbrains.jet.lang.types.JetType;
public class StringValue extends CompileTimeConstant<String> {
public StringValue(String value, boolean canBeUsedInAnnotations) {
super(value, canBeUsedInAnnotations);
public StringValue(String value, boolean canBeUsedInAnnotations, boolean usesVariableAsConstant) {
super(value, canBeUsedInAnnotations, false, usesVariableAsConstant);
}
@NotNull
@@ -242,6 +242,7 @@ fun main(args: Array<String>) {
testClass(javaClass<AbstractEvaluateExpressionTest>()) {
model("evaluate/constant", testMethod = "doConstantTest")
model("evaluate/isPure", testMethod = "doIsPureTest")
model("evaluate/usesVariableAsConstant", testMethod = "doUsesVariableAsConstantTest")
}
}