Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

KT-4569: Intention to simplify boolean expressions with constants #392

Merged
merged 2 commits into from
Apr 10, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public CompileTimeConstant<?> getInitializerConstant(@NotNull JavaField field, @
evaluatedExpression,
DescriptorUtils.isPropertyCompileTimeConstant(descriptor),
false,
true,
descriptor.getType());
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet
trace.record(BindingContext.COMPILE_TIME_VALUE, expression, compileTimeConstant)
return compileTimeConstant
}

return null
}

Expand All @@ -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<*>? {
Expand All @@ -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<*>? {
Expand All @@ -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) {
Expand All @@ -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
}

Expand Down Expand Up @@ -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)
Expand All @@ -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

Expand All @@ -200,20 +206,24 @@ 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)
}
}
}

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) {
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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)
}

Expand All @@ -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)
}
}
Expand Down Expand Up @@ -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()}")
}
Expand All @@ -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")
}
Expand All @@ -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
Expand Down Expand Up @@ -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>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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;
Expand Down Expand Up @@ -282,11 +283,20 @@ public static CompileTimeConstant<?> getAnnotationArgumentValue(
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@

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;
Expand Down Expand Up @@ -129,12 +127,10 @@ public static boolean canBeReducedToBooleanConstant(
@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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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]:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
fun main() {
while(1 > 0) {
while(0 > 1) {
2
}
}

fun dowhile() {
do {return}
while(1 > 0)
while(0 > 1)
}
Loading