Support toInt, toChar and etc as CompileTimeConstants

This commit is contained in:
Natalia Ukhorskaya
2013-11-06 12:27:56 +04:00
parent f218bd1df2
commit 0b1dd2d2be
14 changed files with 297 additions and 76 deletions
@@ -83,6 +83,10 @@ public class ConstantExpressionEvaluator extends JetVisitor<CompileTimeConstant<
@Override
public CompileTimeConstant<?> visitQualifiedExpression(@NotNull JetQualifiedExpression expression, Void data) {
CompileTimeConstant<?> compileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
if (compileTimeConstant != null) {
return compileTimeConstant;
}
JetExpression selectorExpression = expression.getSelectorExpression();
if (selectorExpression != null) {
return selectorExpression.accept(this, null);
@@ -34,7 +34,7 @@ import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResults;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsImpl;
import org.jetbrains.jet.lang.resolve.calls.results.OverloadResolutionResultsUtil;
import org.jetbrains.jet.lang.resolve.calls.util.CallMaker;
import org.jetbrains.jet.lang.resolve.constants.ConstantUtils;
import org.jetbrains.jet.lang.resolve.constants.ConstantsPackage;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.ChainedScope;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
@@ -417,15 +417,15 @@ public class CallExpressionResolver {
context = context.replaceDataFlowInfo(receiverTypeInfo.getDataFlowInfo());
if (selectorExpression instanceof JetSimpleNameExpression) {
ConstantUtils.propagateConstantValues(expression, context.trace, (JetSimpleNameExpression) selectorExpression);
}
JetTypeInfo selectorReturnTypeInfo = getSelectorReturnTypeInfo(
new ExpressionReceiver(receiverExpression, receiverType),
expression.getOperationTokenNode(), selectorExpression, context);
JetType selectorReturnType = selectorReturnTypeInfo.getType();
if (receiverExpression instanceof JetConstantExpression && selectorExpression instanceof JetCallExpression) {
ConstantsPackage.propagateConstantValues(expression, context.trace, (JetCallExpression) selectorExpression);
}
//TODO move further
if (!(receiverType instanceof NamespaceType) && expression.getOperationSign() == JetTokens.SAFE_ACCESS) {
if (selectorReturnType != null && !selectorReturnType.isNullable() && !KotlinBuiltIns.getInstance().isUnit(selectorReturnType)) {
@@ -1,71 +0,0 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.resolve.constants;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetQualifiedExpression;
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.calls.context.ResolutionContext;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeInfo;
import org.jetbrains.jet.lang.types.TypeUtils;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import static org.jetbrains.jet.lang.types.expressions.OperatorConventions.*;
public class ConstantUtils {
public static void propagateConstantValues(JetQualifiedExpression expression, BindingTrace trace, JetSimpleNameExpression selectorExpression) {
JetExpression receiverExpression = expression.getReceiverExpression();
CompileTimeConstant<?> receiverValue = trace.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, receiverExpression);
CompileTimeConstant<?> wholeExpressionValue = trace.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression);
DeclarationDescriptor declarationDescriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, selectorExpression);
if (wholeExpressionValue == null && receiverValue != null && !(receiverValue instanceof ErrorValue) && receiverValue.getValue() instanceof Number
&& KotlinBuiltIns.getInstance().getNumber() == declarationDescriptor) {
Number value = (Number) receiverValue.getValue();
Name referencedName = selectorExpression.getReferencedNameAsName();
if (OperatorConventions.NUMBER_CONVERSIONS.contains(referencedName)) {
if (DOUBLE.equals(referencedName)) {
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new DoubleValue(value.doubleValue()));
}
else if (FLOAT.equals(referencedName)) {
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new FloatValue(value.floatValue()));
}
else if (LONG.equals(referencedName)) {
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new LongValue(value.longValue()));
}
else if (SHORT.equals(referencedName)) {
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new ShortValue(value.shortValue()));
}
else if (BYTE.equals(referencedName)) {
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new ByteValue(value.byteValue()));
}
else if (INT.equals(referencedName)) {
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, new IntValue(value.intValue()));
}
}
}
}
}
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.resolve.constants
import org.jetbrains.jet.lang.psi.JetQualifiedExpression
import org.jetbrains.jet.lang.psi.JetCallExpression
import org.jetbrains.jet.lang.resolve.BindingTrace
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.types.expressions.OperatorConventions
public fun propagateConstantValues(expression : JetQualifiedExpression, trace : BindingTrace, selectorExpression : JetCallExpression) {
val wholeExpressionValue = trace.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression)
if (wholeExpressionValue != null) {
return
}
val receiverExpression = expression.getReceiverExpression()
val receiverValue = trace.getBindingContext().get(BindingContext.COMPILE_TIME_VALUE, receiverExpression)
if (receiverValue == null || receiverValue is ErrorValue) {
return
}
val calleeExpression = selectorExpression.getCalleeExpression()
if (calleeExpression !is JetSimpleNameExpression) {
return
}
val declarationDescriptor = trace.getBindingContext().get(BindingContext.REFERENCE_TARGET, calleeExpression)
if (declarationDescriptor !is FunctionDescriptor) {
return
}
val returnType = declarationDescriptor.getReturnType()
if (returnType == null || !KotlinBuiltIns.getInstance().isPrimitiveType(returnType)) {
return
}
val referencedName = calleeExpression.getReferencedNameAsName()
val value = receiverValue.getValue()
val compileTimeValue = when (value) {
is Number -> {
when(referencedName) {
OperatorConventions.DOUBLE -> DoubleValue(value.toDouble())
OperatorConventions.FLOAT -> FloatValue(value.toFloat())
OperatorConventions.LONG -> LongValue(value.toLong())
OperatorConventions.SHORT -> ShortValue(value.toShort())
OperatorConventions.BYTE -> ByteValue(value.toByte())
OperatorConventions.INT -> IntValue(value.toInt())
OperatorConventions.CHAR -> CharValue(value.toInt().toChar())
}
}
is Char -> {
when(referencedName) {
OperatorConventions.DOUBLE -> DoubleValue(value.toChar().toDouble())
OperatorConventions.FLOAT -> FloatValue(value.toChar().toFloat())
OperatorConventions.LONG -> LongValue(value.toChar().toLong())
OperatorConventions.SHORT -> ShortValue(value.toChar().toShort())
OperatorConventions.BYTE -> ByteValue(value.toChar().toByte())
OperatorConventions.INT -> IntValue(value.toChar().toInt())
OperatorConventions.CHAR -> CharValue(value.toChar())
}
}
else -> null
}
if (compileTimeValue != null) {
trace.record(BindingContext.COMPILE_TIME_VALUE, expression,compileTimeValue)
}
}
@@ -0,0 +1,12 @@
package test
annotation class Ann(
val b1: Byte,
val b2: Byte,
val b3: Byte,
val b4: Byte
)
Ann(1, 1.toByte(), 128.toByte(), 128) class MyClass
// EXPECTED: Ann[b1 = 1.toByte(): jet.Byte, b2 = 1.toByte(): jet.Byte, b3 = -128.toByte(): jet.Byte, b4 = 128.toInt(): jet.Int]
@@ -0,0 +1,16 @@
package test
annotation class Ann(
val b1: Char,
val b2: Char,
val b3: Int,
val b4: Long,
val b5: Byte,
val b6: Short,
val b7: Double,
val b8: Float
)
Ann('c', 99.toChar(), 'c'.toInt(), 'c'.toLong(), 'c'.toByte(), 'c'.toShort(), 'c'.toDouble(), 'c'.toFloat()) class MyClass
// EXPECTED: Ann[b1 = #99(c): jet.Char, b2 = #99(c): jet.Char, b3 = 99.toInt(): jet.Int, b4 = 99.toLong(): jet.Long, b5 = 99.toByte(): jet.Byte, b6 = 99.toShort(): jet.Short, b7 = 99.0.toDouble(): jet.Double, b8 = 99.0.toFloat(): jet.Float]
@@ -0,0 +1,12 @@
package test
annotation class Ann(
val b1: Double,
val b2: Double,
val b3: Double,
val b4: Double
)
Ann(1.0, 1.toDouble(), 1.7976931348623157E309.toDouble(), 1.7976931348623157E309) class MyClass
// EXPECTED: Ann[b1 = 1.0.toDouble(): jet.Double, b2 = 1.0.toDouble(): jet.Double, b3 = Infinity.toDouble(): jet.Double, b4 = Infinity.toDouble(): jet.Double]
@@ -0,0 +1,12 @@
package test
annotation class Ann(
val b1: Float,
val b2: Float,
val b3: Float,
val b4: Float
)
Ann(1.0, 1.toFloat(), 1.0.toFloat()) class MyClass
// EXPECTED: Ann[b1 = 1.0.toFloat(): jet.Float, b2 = 1.0.toFloat(): jet.Float, b3 = 1.0.toFloat(): jet.Float]
@@ -0,0 +1,12 @@
package test
annotation class Ann(
val b1: Int,
val b2: Int,
val b3: Int,
val b4: Int
)
Ann(1, 1.toInt(), 2147483648.toInt(), 2147483648) class MyClass
// EXPECTED: Ann[b1 = 1.toInt(): jet.Int, b2 = 1.toInt(): jet.Int, b3 = -2147483648.toInt(): jet.Int, b4 = 2147483648.toLong(): jet.Long]
@@ -0,0 +1,10 @@
package test
annotation class Ann(
val b1: Long,
val b2: Long
)
Ann(1, 1.toLong()) class MyClass
// EXPECTED: Ann[b1 = 1.toLong(): jet.Long, b2 = 1.toLong(): jet.Long]
@@ -0,0 +1,12 @@
package test
annotation class Ann(
val b1: Short,
val b2: Short,
val b3: Short,
val b4: Short
)
Ann(1, 1.toShort(), 32768.toShort(), 32768) class MyClass
// EXPECTED: Ann[b1 = 1.toShort(): jet.Short, b2 = 1.toShort(): jet.Short, b3 = -32768.toShort(): jet.Short, b4 = 32768.toInt(): jet.Int]
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.resolve.annotation
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.jet.InTextDirectivesUtils
import java.io.File
public abstract class AbstractAnnotationParameterTest : AbstractAnnotationDescriptorResolveTest() {
fun doTest(path: String) {
val fileText = FileUtil.loadFile(File(path))
val namespaceDescriptor = getNamespaceDescriptor(fileText)
val classDescriptor = AbstractAnnotationDescriptorResolveTest.getClassDescriptor(namespaceDescriptor, "MyClass")
val expected = InTextDirectivesUtils.findListWithPrefixes(fileText, "// EXPECTED: ").makeString(", ")
AbstractAnnotationDescriptorResolveTest.checkDescriptor(expected, classDescriptor)
}
}
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.resolve.annotation;
import junit.framework.Assert;
import junit.framework.Test;
import junit.framework.TestSuite;
import java.io.File;
import java.util.regex.Pattern;
import org.jetbrains.jet.JetTestUtils;
import org.jetbrains.jet.test.InnerTestClasses;
import org.jetbrains.jet.test.TestMetadata;
import org.jetbrains.jet.resolve.annotation.AbstractAnnotationParameterTest;
/** This class is generated by {@link org.jetbrains.jet.generators.tests.GenerateTests}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("compiler/testData/resolveAnnotations/parameters")
public class AnnotationParameterTestGenerated extends AbstractAnnotationParameterTest {
public void testAllFilesPresentInParameters() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/resolveAnnotations/parameters"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("byte.kt")
public void testByte() throws Exception {
doTest("compiler/testData/resolveAnnotations/parameters/byte.kt");
}
@TestMetadata("char.kt")
public void testChar() throws Exception {
doTest("compiler/testData/resolveAnnotations/parameters/char.kt");
}
@TestMetadata("double.kt")
public void testDouble() throws Exception {
doTest("compiler/testData/resolveAnnotations/parameters/double.kt");
}
@TestMetadata("float.kt")
public void testFloat() throws Exception {
doTest("compiler/testData/resolveAnnotations/parameters/float.kt");
}
@TestMetadata("int.kt")
public void testInt() throws Exception {
doTest("compiler/testData/resolveAnnotations/parameters/int.kt");
}
@TestMetadata("long.kt")
public void testLong() throws Exception {
doTest("compiler/testData/resolveAnnotations/parameters/long.kt");
}
@TestMetadata("short.kt")
public void testShort() throws Exception {
doTest("compiler/testData/resolveAnnotations/parameters/short.kt");
}
}
@@ -64,6 +64,7 @@ import org.jetbrains.jet.psi.AbstractJetPsiMatcherTest;
import org.jetbrains.jet.resolve.AbstractResolveBaseTest;
import org.jetbrains.jet.resolve.AbstractResolveTest;
import org.jetbrains.jet.resolve.AbstractResolveWithLibTest;
import org.jetbrains.jet.resolve.annotation.AbstractAnnotationParameterTest;
import org.jetbrains.jet.safeDelete.AbstractJetSafeDeleteTest;
import java.io.File;
@@ -557,6 +558,13 @@ public class GenerateTests {
AbstractRenameTest.class,
new SingleClassTestModel(new File("idea/testData/refactoring/rename"), Pattern.compile("^(.+)\\.test$"), "doTest")
);
generateTest(
"compiler/tests",
"AnnotationParameterTestGenerated",
AbstractAnnotationParameterTest.class,
testModel("compiler/testData/resolveAnnotations/parameters")
);
}
private static SimpleTestClassModel testModel(@NotNull String rootPath) {