Introduce new feature: division by zero in constant expressions

This commit is contained in:
Mikhail Zarechenskiy
2016-12-02 09:58:09 +03:00
parent 3e4376f4ba
commit 160d43fe2e
10 changed files with 45 additions and 13 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -45,7 +45,6 @@ import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
@@ -92,8 +91,6 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
private DefaultSourceMapper sourceMapper;
private final ConstantExpressionEvaluator constantExpressionEvaluator;
public MemberCodegen(
@NotNull GenerationState state,
@Nullable MemberCodegen<?> parentCodegen,
@@ -111,7 +108,6 @@ public abstract class MemberCodegen<T extends KtElement/* TODO: & JetDeclaration
this.functionCodegen = new FunctionCodegen(context, v, state, this);
this.propertyCodegen = new PropertyCodegen(context, v, functionCodegen, this);
this.parentCodegen = parentCodegen;
this.constantExpressionEvaluator = new ConstantExpressionEvaluator(state.getModule().getBuiltIns());
}
protected MemberCodegen(@NotNull MemberCodegen<T> wrapped, T declaration, FieldOwnerContext codegenContext) {
@@ -21,6 +21,8 @@ import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.text.LiteralFormatUtil
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.diagnostics.Errors
@@ -44,7 +46,8 @@ import java.math.BigInteger
import java.util.*
class ConstantExpressionEvaluator(
internal val builtIns: KotlinBuiltIns
internal val builtIns: KotlinBuiltIns,
internal val languageVersionSettings: LanguageVersionSettings
) {
internal val constantValueFactory = ConstantValueFactory(builtIns)
@@ -456,7 +459,8 @@ private class ConstantExpressionEvaluatorVisitor(
val parentExpression: KtExpression = PsiTreeUtil.getParentOfType(receiverExpression, KtExpression::class.java)!!
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression))
if (isIntegerType(argumentForReceiver.value) && isIntegerType(argumentForParameter.value)) {
if ((isIntegerType(argumentForReceiver.value) && isIntegerType(argumentForParameter.value)) ||
!constantExpressionEvaluator.languageVersionSettings.supportsFeature(LanguageFeature.DivisionByZeroInConstantExpressions)) {
return factory.createErrorValue("Division by zero").wrap()
}
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.script
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.resolve.BindingTraceContext
@@ -42,7 +43,7 @@ internal class KtAnnotationWrapper(val psi: KtAnnotationEntry, val targetClass:
// TODO: annotation constructors unsupported yet in kotlin reflection, test and correct when they will be ready
val targetAnnParams = targetClass.primaryConstructor?.parameters
psi.valueArguments.mapIndexed { i, arg ->
val evaluator = ConstantExpressionEvaluator(DefaultBuiltIns.Instance)
val evaluator = ConstantExpressionEvaluator(DefaultBuiltIns.Instance, LanguageVersionSettingsImpl.DEFAULT)
val trace = BindingTraceContext()
val result = evaluator.evaluateToConstantValue(arg.getArgumentExpression()!!, trace, TypeUtils.NO_EXPECTED_TYPE)
// TODO: consider inspecting `trace` to find diagnostics reported during the computation (such as division by zero, integer overflow, invalid annotation parameters etc.)
@@ -0,0 +1,11 @@
// !LANGUAGE: -DivisionByZeroInConstantExpressions
// !DIAGNOSTICS:-DIVISION_BY_ZERO
const val a = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>1 / 0.0<!>
const val b = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>1.0 / 0<!>
const val c = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>0.0 / 0<!>
const val i = <!CONST_VAL_WITH_NON_CONST_INITIALIZER!>1 / 0<!>
val nonConst1 = 1.0 / 0
val nonConst2 = 1 / 0
@@ -0,0 +1,8 @@
package
public const val a: kotlin.Double
public const val b: kotlin.Double
public const val c: kotlin.Double
public const val i: kotlin.Int
public val nonConst1: kotlin.Double
public val nonConst2: kotlin.Int
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve.constants.evaluate
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext
@@ -64,7 +65,7 @@ abstract class AbstractCompileTimeConstantEvaluatorTest : AbstractAnnotationDesc
private fun evaluateInitializer(context: BindingContext, property: VariableDescriptor): CompileTimeConstant<*>? {
val propertyDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(property) as KtProperty
val compileTimeConstant = ConstantExpressionEvaluator(property.builtIns).evaluateExpression(
val compileTimeConstant = ConstantExpressionEvaluator(property.builtIns, LanguageVersionSettingsImpl.DEFAULT).evaluateExpression(
propertyDeclaration.initializer!!,
DelegatingBindingTrace(context, "trace for evaluating compile time constant"),
property.type
@@ -12261,6 +12261,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("noDivisionByZeroFeature.kt")
public void testNoDivisionByZeroFeature() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/modifiers/const/noDivisionByZeroFeature.kt");
doTest(fileName);
}
@TestMetadata("types.kt")
public void testTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/modifiers/const/types.kt");
@@ -33,6 +33,7 @@ enum class LanguageFeature(val sinceVersion: LanguageVersion?) {
SingleUnderscoreForParameterName(KOTLIN_1_1),
DslMarkersSupport(KOTLIN_1_1),
UnderscoresInNumericLiterals(KOTLIN_1_1),
DivisionByZeroInConstantExpressions(KOTLIN_1_1),
// Experimental features
MultiPlatformProjects(null),
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
* Copyright 2010-2016 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.
@@ -20,6 +20,7 @@ import com.intellij.openapi.util.Key
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.util.getResolutionScope
@@ -54,7 +55,8 @@ class ExtractableSubstringInfo(
val tempContext = expr.analyzeInContext(scope, template)
val trace = DelegatingBindingTrace(tempContext, "Evaluate '$literal'")
val value = ConstantExpressionEvaluator(builtIns).evaluateExpression(expr, trace)
val languageVersionSettings = facade.getFrontendService(LanguageVersionSettings::class.java)
val value = ConstantExpressionEvaluator(builtIns, languageVersionSettings).evaluateExpression(expr, trace)
if (value == null || value.isError) return stringType
return value.toConstantValue(TypeUtils.NO_EXPECTED_TYPE).type
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiExpression
import com.intellij.psi.impl.ConstantExpressionEvaluator
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.project.languageVersionSettings
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.constants.ArrayValue
@@ -53,7 +54,8 @@ class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
return when (expressionToCompute) {
is KtExpression -> {
val resolutionFacade = expressionToCompute.getResolutionFacade()
val evaluator = FrontendConstantExpressionEvaluator(resolutionFacade.moduleDescriptor.builtIns)
val evaluator = FrontendConstantExpressionEvaluator(resolutionFacade.moduleDescriptor.builtIns,
expressionToCompute.languageVersionSettings)
val evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument")
val constant = evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) ?: return null