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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.BindingContextUtils;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.constants.ConstantValue; 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.descriptorUtil.DescriptorUtilsKt;
import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt; 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 DefaultSourceMapper sourceMapper;
private final ConstantExpressionEvaluator constantExpressionEvaluator;
public MemberCodegen( public MemberCodegen(
@NotNull GenerationState state, @NotNull GenerationState state,
@Nullable MemberCodegen<?> parentCodegen, @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.functionCodegen = new FunctionCodegen(context, v, state, this);
this.propertyCodegen = new PropertyCodegen(context, v, functionCodegen, this); this.propertyCodegen = new PropertyCodegen(context, v, functionCodegen, this);
this.parentCodegen = parentCodegen; this.parentCodegen = parentCodegen;
this.constantExpressionEvaluator = new ConstantExpressionEvaluator(state.getModule().getBuiltIns());
} }
protected MemberCodegen(@NotNull MemberCodegen<T> wrapped, T declaration, FieldOwnerContext codegenContext) { 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 com.intellij.util.text.LiteralFormatUtil
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.builtins.KotlinBuiltIns 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.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
@@ -44,7 +46,8 @@ import java.math.BigInteger
import java.util.* import java.util.*
class ConstantExpressionEvaluator( class ConstantExpressionEvaluator(
internal val builtIns: KotlinBuiltIns internal val builtIns: KotlinBuiltIns,
internal val languageVersionSettings: LanguageVersionSettings
) { ) {
internal val constantValueFactory = ConstantValueFactory(builtIns) internal val constantValueFactory = ConstantValueFactory(builtIns)
@@ -456,7 +459,8 @@ private class ConstantExpressionEvaluatorVisitor(
val parentExpression: KtExpression = PsiTreeUtil.getParentOfType(receiverExpression, KtExpression::class.java)!! val parentExpression: KtExpression = PsiTreeUtil.getParentOfType(receiverExpression, KtExpression::class.java)!!
trace.report(Errors.DIVISION_BY_ZERO.on(parentExpression)) 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() return factory.createErrorValue("Division by zero").wrap()
} }
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.script package org.jetbrains.kotlin.script
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtUserType import org.jetbrains.kotlin.psi.KtUserType
import org.jetbrains.kotlin.resolve.BindingTraceContext 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 // TODO: annotation constructors unsupported yet in kotlin reflection, test and correct when they will be ready
val targetAnnParams = targetClass.primaryConstructor?.parameters val targetAnnParams = targetClass.primaryConstructor?.parameters
psi.valueArguments.mapIndexed { i, arg -> psi.valueArguments.mapIndexed { i, arg ->
val evaluator = ConstantExpressionEvaluator(DefaultBuiltIns.Instance) val evaluator = ConstantExpressionEvaluator(DefaultBuiltIns.Instance, LanguageVersionSettingsImpl.DEFAULT)
val trace = BindingTraceContext() val trace = BindingTraceContext()
val result = evaluator.evaluateToConstantValue(arg.getArgumentExpression()!!, trace, TypeUtils.NO_EXPECTED_TYPE) 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.) // 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 package org.jetbrains.kotlin.resolve.constants.evaluate
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.VariableDescriptor import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
@@ -64,7 +65,7 @@ abstract class AbstractCompileTimeConstantEvaluatorTest : AbstractAnnotationDesc
private fun evaluateInitializer(context: BindingContext, property: VariableDescriptor): CompileTimeConstant<*>? { private fun evaluateInitializer(context: BindingContext, property: VariableDescriptor): CompileTimeConstant<*>? {
val propertyDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(property) as KtProperty val propertyDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(property) as KtProperty
val compileTimeConstant = ConstantExpressionEvaluator(property.builtIns).evaluateExpression( val compileTimeConstant = ConstantExpressionEvaluator(property.builtIns, LanguageVersionSettingsImpl.DEFAULT).evaluateExpression(
propertyDeclaration.initializer!!, propertyDeclaration.initializer!!,
DelegatingBindingTrace(context, "trace for evaluating compile time constant"), DelegatingBindingTrace(context, "trace for evaluating compile time constant"),
property.type property.type
@@ -12261,6 +12261,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); 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") @TestMetadata("types.kt")
public void testTypes() throws Exception { public void testTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/modifiers/const/types.kt"); 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), SingleUnderscoreForParameterName(KOTLIN_1_1),
DslMarkersSupport(KOTLIN_1_1), DslMarkersSupport(KOTLIN_1_1),
UnderscoresInNumericLiterals(KOTLIN_1_1), UnderscoresInNumericLiterals(KOTLIN_1_1),
DivisionByZeroInConstantExpressions(KOTLIN_1_1),
// Experimental features // Experimental features
MultiPlatformProjects(null), 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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.openapi.util.TextRange
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.idea.analysis.analyzeInContext import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.getResolutionScope
@@ -54,7 +55,8 @@ class ExtractableSubstringInfo(
val tempContext = expr.analyzeInContext(scope, template) val tempContext = expr.analyzeInContext(scope, template)
val trace = DelegatingBindingTrace(tempContext, "Evaluate '$literal'") 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 if (value == null || value.isError) return stringType
return value.toConstantValue(TypeUtils.NO_EXPECTED_TYPE).type return value.toConstantValue(TypeUtils.NO_EXPECTED_TYPE).type
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiExpression
import com.intellij.psi.impl.ConstantExpressionEvaluator import com.intellij.psi.impl.ConstantExpressionEvaluator
import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation import org.jetbrains.kotlin.asJava.elements.KtLightAnnotation
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade 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.psi.KtExpression
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.constants.ArrayValue import org.jetbrains.kotlin.resolve.constants.ArrayValue
@@ -53,7 +54,8 @@ class KotlinLightConstantExpressionEvaluator : ConstantExpressionEvaluator {
return when (expressionToCompute) { return when (expressionToCompute) {
is KtExpression -> { is KtExpression -> {
val resolutionFacade = expressionToCompute.getResolutionFacade() 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 evaluatorTrace = DelegatingBindingTrace(resolutionFacade.analyze(expressionToCompute), "Evaluating annotation argument")
val constant = evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) ?: return null val constant = evaluator.evaluateExpression(expressionToCompute, evaluatorTrace) ?: return null