diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index f990c8142f7..c5853937e3a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -65,6 +65,7 @@ import org.jetbrains.kotlin.resolve.calls.util.CallMaker; import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant; +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.resolve.constants.evaluate.EvaluatePackage; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage; import org.jetbrains.kotlin.resolve.inline.InlineUtil; @@ -1286,7 +1287,7 @@ public class ExpressionCodegen extends JetVisitor implem @Nullable public static CompileTimeConstant getCompileTimeConstant(@NotNull JetExpression expression, @NotNull BindingContext bindingContext) { - CompileTimeConstant compileTimeValue = bindingContext.get(COMPILE_TIME_VALUE, expression); + CompileTimeConstant compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, bindingContext); if (compileTimeValue instanceof IntegerValueTypeConstant) { JetType expectedType = bindingContext.getType(expression); assert expectedType != null : "Expression is not type checked: " + expression.getText(); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java index 5832c02f8f8..55eb2f6728f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetControlFlowProcessor.java @@ -42,6 +42,7 @@ import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; import org.jetbrains.kotlin.resolve.*; import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver; @@ -315,7 +316,7 @@ public class JetControlFlowProcessor { @Override public void visitConstantExpression(@NotNull JetConstantExpression expression) { - CompileTimeConstant constant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression); + CompileTimeConstant constant = ConstantExpressionEvaluator.getConstant(expression, trace.getBindingContext()); builder.loadConstant(expression, constant); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 6a738ebe9b3..92751e6152a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -552,6 +552,7 @@ public interface Errors { // Compile-time values + DiagnosticFactory0 DIVISION_BY_ZERO = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 INTEGER_OVERFLOW = DiagnosticFactory0.create(WARNING); DiagnosticFactory0 WRONG_LONG_SUFFIX = DiagnosticFactory0.create(ERROR, LONG_LITERAL_SUFFIX); DiagnosticFactory0 INT_LITERAL_OUT_OF_RANGE = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 13e975d5fa4..8dfc810cd07 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -366,6 +366,7 @@ public class DefaultErrorMessages { MAP.put(TOO_MANY_ARGUMENTS, "Too many arguments for {0}", FQ_NAMES_IN_TYPES); MAP.put(CONSTANT_EXPECTED_TYPE_MISMATCH, "An {0} literal does not conform to the expected type {1}", STRING, RENDER_TYPE); + MAP.put(DIVISION_BY_ZERO, "Division by zero"); MAP.put(INTEGER_OVERFLOW, "This operation has led to an overflow"); MAP.put(INT_LITERAL_OUT_OF_RANGE, "The value is out of range"); MAP.put(WRONG_LONG_SUFFIX, "Use 'L' instead of 'l'"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java index c9bc57cea67..c3f3aeac128 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java @@ -298,7 +298,7 @@ public class AnnotationResolver { } } - CompileTimeConstant constant = trace.get(BindingContext.COMPILE_TIME_VALUE, argumentExpression); + CompileTimeConstant constant = ConstantExpressionEvaluator.getConstant(argumentExpression, trace.getBindingContext()); if (constant != null && constant.canBeUsedInAnnotations()) { return; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt index f9c783127e3..df56946a052 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/constants/evaluate/ConstantExpressionEvaluator.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.resolve.constants.evaluate +import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.psi.* @@ -30,8 +31,10 @@ import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument import org.jetbrains.kotlin.JetNodeTypes +import org.jetbrains.kotlin.diagnostics.DiagnosticUtils import java.math.BigInteger import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import kotlin.platform.platformStatic @@ -41,7 +44,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet companion object { platformStatic public fun evaluate(expression: JetExpression, trace: BindingTrace, expectedType: JetType? = TypeUtils.NO_EXPECTED_TYPE): CompileTimeConstant<*>? { val evaluator = ConstantExpressionEvaluator(trace) - return evaluator.evaluate(expression, expectedType) + val constant = evaluator.evaluate(expression, expectedType) + return if (constant !is ErrorValue) constant else null } platformStatic public fun isPropertyCompileTimeConstant(descriptor: VariableDescriptor): Boolean { @@ -55,10 +59,19 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet } return false } + + platformStatic public fun getConstant(expression: JetExpression, bindingContext: BindingContext): CompileTimeConstant<*>? { + val constant = getPossiblyErrorConstant(expression, bindingContext) + return if (constant !is ErrorValue) constant else null + } + + platformStatic private fun getPossiblyErrorConstant(expression: JetExpression, bindingContext: BindingContext): CompileTimeConstant<*>? { + return bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression) + } } private fun evaluate(expression: JetExpression, expectedType: JetType?): CompileTimeConstant<*>? { - val recordedCompileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression) + val recordedCompileTimeConstant = getPossiblyErrorConstant(expression, trace.getBindingContext()) if (recordedCompileTimeConstant != null) { return recordedCompileTimeConstant } @@ -213,6 +226,8 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet if (argumentForParameter == null) return null if (isDivisionByZero(resultingDescriptorName.asString(), argumentForParameter.value)) { + val parentExpression: JetExpression = PsiTreeUtil.getParentOfType(receiverExpression, javaClass()) + trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression)) return ErrorValue.create("Division by zero") } @@ -235,12 +250,12 @@ 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 usesVariableAsConstant(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.usesVariableAsConstant() ?: false - private fun canBeUsedInAnnotation(expression: JetExpression) = trace.get(BindingContext.COMPILE_TIME_VALUE, expression)?.canBeUsedInAnnotations() ?: false + private fun canBeUsedInAnnotation(expression: JetExpression) = getConstant(expression, trace.getBindingContext())?.canBeUsedInAnnotations() ?: false private fun isPureConstant(expression: JetExpression): Boolean { - val compileTimeConstant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression) + val compileTimeConstant = getConstant(expression, trace.getBindingContext()) if (compileTimeConstant is IntegerValueConstant) { return compileTimeConstant.isPure() } @@ -444,7 +459,7 @@ public class ConstantExpressionEvaluator private (val trace: BindingTrace) : Jet } private fun createOperationArgument(expression: JetExpression, expressionType: JetType, compileTimeType: CompileTimeType<*>): OperationArgument? { - val evaluatedConstant = evaluate(expression, expressionType) + val evaluatedConstant = evaluate(expression, trace, expressionType) if (evaluatedConstant == null) return null if (evaluatedConstant is IntegerValueTypeConstant) { diff --git a/compiler/testData/codegen/box/binaryOp/divisionByZero.kt b/compiler/testData/codegen/box/binaryOp/divisionByZero.kt new file mode 100644 index 00000000000..c94a7fe39bb --- /dev/null +++ b/compiler/testData/codegen/box/binaryOp/divisionByZero.kt @@ -0,0 +1,12 @@ +fun box(): String { + val a1 = 0 + val a2 = try { 1 / 0 } catch(e: ArithmeticException) { 0 } + val a3 = try { 1 / a1 } catch(e: ArithmeticException) { 0 } + val a4 = try { 1 / a2 } catch(e: ArithmeticException) { 0 } + val a5 = try { 2 * (1 / 0) } catch(e: ArithmeticException) { 0 } + val a6 = try { 2 * 1 / 0 } catch(e: ArithmeticException) { 0 } + + try { val s1 = "${2 * (1 / 0) }" } catch(e: ArithmeticException) { } + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/evaluate/divisionByZero.kt b/compiler/testData/diagnostics/tests/evaluate/divisionByZero.kt new file mode 100644 index 00000000000..051386ea5fc --- /dev/null +++ b/compiler/testData/diagnostics/tests/evaluate/divisionByZero.kt @@ -0,0 +1,17 @@ +// !DIAGNOSTICS: -UNUSED_VARIABLE + +val a1 = 0 +val a2 = 1 / 0 +val a3 = 1 / a1 +val a4 = 1 / a2 +val a5 = 2 * (1 / 0) + +val a6 = 1.div(0) +val a7 = 1.div(a1) +val a8 = 1.div(a2) +val a9 = 2 * (1.div(0)) + +val b1: Byte = 1 / 0 +Ann(1 / 0) val b2 = 1 + +annotation class Ann(val i : Int) \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/evaluate/divisionByZero.txt b/compiler/testData/diagnostics/tests/evaluate/divisionByZero.txt new file mode 100644 index 00000000000..5e7d7081d39 --- /dev/null +++ b/compiler/testData/diagnostics/tests/evaluate/divisionByZero.txt @@ -0,0 +1,21 @@ +package + +internal val a1: kotlin.Int = 0 +internal val a2: kotlin.Int +internal val a3: kotlin.Int +internal val a4: kotlin.Int +internal val a5: kotlin.Int +internal val a6: kotlin.Int +internal val a7: kotlin.Int +internal val a8: kotlin.Int +internal val a9: kotlin.Int +internal val b1: kotlin.Byte +Ann() internal val b2: kotlin.Int = 1 + +internal final annotation class Ann : kotlin.Annotation { + public constructor Ann(/*0*/ i: kotlin.Int) + internal final val i: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 0b3b15db332..f5a133fa424 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -4404,6 +4404,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { doTest(fileName); } + @TestMetadata("divisionByZero.kt") + public void testDivisionByZero() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/evaluate/divisionByZero.kt"); + doTest(fileName); + } + @TestMetadata("float.kt") public void testFloat() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/evaluate/float.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java index 973c395ec55..4537a3c61cd 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -262,6 +262,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("divisionByZero.kt") + public void testDivisionByZero() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/divisionByZero.kt"); + doTest(fileName); + } + @TestMetadata("infixCall.kt") public void testInfixCall() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/binaryOp/infixCall.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 8ab76ed82fe..c14229458f8 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.bindingContextUtil.BindingContextUtilPackage; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.NullValue; +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeUtils; @@ -71,7 +72,7 @@ public final class ExpressionVisitor extends TranslatorVisitor { @NotNull private static JsNode translateConstantExpression(@NotNull JetConstantExpression expression, @NotNull TranslationContext context) { - CompileTimeConstant compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression); + CompileTimeConstant compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext()); assert compileTimeValue != null : message(expression, "Expression is not compile time value: " + expression.getText() + " "); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java index bc4fb748931..951a1344628 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/operation/UnaryOperationTranslator.java @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.resolve.BindingContext; import org.jetbrains.kotlin.resolve.BindingContextUtils; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.types.JetType; import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression; @@ -63,7 +64,7 @@ public final class UnaryOperationTranslator { if (operationToken == JetTokens.MINUS) { JetExpression baseExpression = getBaseExpression(expression); if (baseExpression instanceof JetConstantExpression) { - CompileTimeConstant compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression); + CompileTimeConstant compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context.bindingContext()); assert compileTimeValue != null : message(expression, "Expression is not compile time value: " + expression.getText() + " "); Object value = getCompileTimeValue(context.bindingContext(), expression, compileTimeValue); if (value instanceof Long) { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java index 09b5b127e81..80acae1d609 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/BindingUtils.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant; import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant; +import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator; import org.jetbrains.kotlin.types.JetType; import org.jetbrains.kotlin.types.TypeUtils; @@ -149,7 +150,7 @@ public final class BindingUtils { @Nullable public static Object getCompileTimeValue(@NotNull BindingContext context, @NotNull JetExpression expression) { - CompileTimeConstant compileTimeValue = context.get(BindingContext.COMPILE_TIME_VALUE, expression); + CompileTimeConstant compileTimeValue = ConstantExpressionEvaluator.getConstant(expression, context); if (compileTimeValue != null) { return getCompileTimeValue(context, expression, compileTimeValue); } diff --git a/js/js.translator/testData/standardClasses/cases/hashMapTypeOfElement.kt b/js/js.translator/testData/standardClasses/cases/hashMapTypeOfElement.kt index 664335836b4..961b3f32b2d 100644 --- a/js/js.translator/testData/standardClasses/cases/hashMapTypeOfElement.kt +++ b/js/js.translator/testData/standardClasses/cases/hashMapTypeOfElement.kt @@ -49,8 +49,8 @@ fun box(): String { mapWithFloatKeys.clear() var fNaN: Float = 0.0f / 0.0f - mapWithFloatKeys[dNaN] = 100 - assertEquals(100, mapWithFloatKeys[dNaN]) + mapWithFloatKeys[fNaN] = 100 + assertEquals(100, mapWithFloatKeys[fNaN]) assertEquals("number", typeof (mapWithFloatKeys.keySet().iterator().next()), "fNaN") mapWithFloatKeys.clear()