diff --git a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 538538e6f37..f832d6a1b52 100644 --- a/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/analysis/low-level-api-fir/tests/org/jetbrains/kotlin/analysis/low/level/api/fir/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -4942,6 +4942,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt"); } + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt"); + } + + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt"); + } + @Test @TestMetadata("equals.kt") public void testEquals() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 8774a7c9aaa..ff9c349f6c4 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -4942,6 +4942,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt"); } + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt"); + } + + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt"); + } + @Test @TestMetadata("equals.kt") public void testEquals() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 95d67e24f6f..f7d82e0ce03 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -4942,6 +4942,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt"); } + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt"); + } + + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt"); + } + @Test @TestMetadata("equals.kt") public void testEquals() throws Exception { diff --git a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index b20b05d3a12..47006fe18e0 100644 --- a/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/cfg/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValu import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange import org.jetbrains.kotlin.contracts.description.canBeRevisited import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited @@ -1321,7 +1322,11 @@ class ControlFlowProcessor( builder.bindLabel(doneLabel) mergeValues(branches, expression) - WhenChecker.checkDuplicatedLabels(expression, trace) + WhenChecker.checkDuplicatedLabels( + expression, + trace, + languageVersionSettings ?: LanguageVersionSettingsImpl.DEFAULT + ) } override fun visitObjectLiteralExpression(expression: KtObjectLiteralExpression) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt index 9b5bb118ff0..a38bcbf4051 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/WhenChecker.kt @@ -18,6 +18,8 @@ package org.jetbrains.kotlin.cfg import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality @@ -357,7 +359,11 @@ object WhenChecker { fun containsNullCase(expression: KtWhenExpression, context: BindingContext) = WhenOnNullableExhaustivenessChecker.getMissingCases(expression, context, true).isEmpty() - fun checkDuplicatedLabels(expression: KtWhenExpression, trace: BindingTrace) { + fun checkDuplicatedLabels( + expression: KtWhenExpression, + trace: BindingTrace, + languageVersionSettings: LanguageVersionSettings, + ) { if (expression.subjectExpression == null) return val checkedTypes = HashSet>() @@ -407,7 +413,7 @@ object WhenChecker { } false -> { // already found bad constant in previous branches - val isTrivial = constant.isTrivial() + val isTrivial = constant.isTrivial(constantExpression, languageVersionSettings) if (isTrivial) { // this constant is trivial -> report on first non trivial constant val reportOn = notTrivialBranches.remove(constant)!! @@ -420,7 +426,7 @@ object WhenChecker { } null -> { // met constant for a first time - val isTrivial = constant.isTrivial() + val isTrivial = constant.isTrivial(constantExpression, languageVersionSettings) checkedConstants[constant] = isTrivial if (!isTrivial) { notTrivialBranches[constant] = constantExpression @@ -446,8 +452,15 @@ object WhenChecker { } } - private fun CompileTimeConstant<*>.isTrivial(): Boolean { - return !usesVariableAsConstant + private fun CompileTimeConstant<*>.isTrivial( + expression: KtExpression, + languageVersionSettings: LanguageVersionSettings + ): Boolean { + if (usesVariableAsConstant) return false + if (!languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSimplificationOfNonTrivialConstBooleanExpressions)) { + return !ConstantExpressionEvaluator.isComplexBooleanConstant(expression, this) + } + return true } fun checkDeprecatedWhenSyntax(trace: BindingTrace, expression: KtWhenExpression) { 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 08a8938fb45..2aaac2826ef 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 @@ -51,7 +51,6 @@ import org.jetbrains.kotlin.types.isError import org.jetbrains.kotlin.types.typeUtil.isBoolean import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.util.OperatorNameConventions -import org.jetbrains.kotlin.utils.addToStdlib.runIf import java.math.BigInteger import java.util.* @@ -369,6 +368,17 @@ class ConstantExpressionEvaluator( KotlinBuiltIns.isArray(type) -> isTypeParameterOrArrayOfTypeParameter(type.arguments.singleOrNull()?.type) else -> type.constructor.declarationDescriptor is TypeParameterDescriptor } + + fun isComplexBooleanConstant( + expression: KtExpression, + constant: CompileTimeConstant<*> + ): Boolean { + if (constant.isError) return false + val constantValue = constant.toConstantValue(constant.moduleDescriptor.builtIns.booleanType) + if (!constantValue.getType(constant.moduleDescriptor).isBoolean()) return false + if (expression is KtConstantExpression || constant.parameters.usesVariableAsConstant) return false + return true + } } } @@ -409,15 +419,13 @@ private class ConstantExpressionEvaluatorVisitor( return null } - @Suppress("warnings") private fun shouldSkipComplexBooleanValue( expression: KtExpression, constant: CompileTimeConstant<*> ): Boolean { - if (constant.isError) return false - val constantValue = constant.toConstantValue(builtIns.booleanType) - if (!constantValue.getType(constantExpressionEvaluator.module).isBoolean()) return false - if (expression is KtConstantExpression || constant.parameters.usesVariableAsConstant) return false + if (!ConstantExpressionEvaluator.isComplexBooleanConstant(expression, constant)) { + return false + } if (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSimplificationOfNonTrivialConstBooleanExpressions)) { return true @@ -430,6 +438,7 @@ private class ConstantExpressionEvaluatorVisitor( parent is KtWhenConditionWithExpression || parent is KtContainerNode && (parent.parent is KtWhileExpression || parent.parent is KtDoWhileExpression) ) { + val constantValue = constant.toConstantValue(builtIns.booleanType) trace.report(Errors.NON_TRIVIAL_BOOLEAN_CONSTANT.on(expression, constantValue.value as Boolean)) } return false diff --git a/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.fir.kt b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.fir.kt new file mode 100644 index 00000000000..0f484062dad --- /dev/null +++ b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.fir.kt @@ -0,0 +1,20 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions + +const val myF = false +const val myT = true + +fun test(someBoolean: Boolean) { + val s = when (someBoolean) { + true || true -> 1 + false && false -> 2 + true -> 3 + false -> 4 + } +} + +fun test_2(someBoolean: Boolean) { + val s = when (someBoolean) { + true || true -> 1 + false && false -> 2 + } +} diff --git a/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt new file mode 100644 index 00000000000..a5596b9ccb8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt @@ -0,0 +1,20 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions + +const val myF = false +const val myT = true + +fun test(someBoolean: Boolean) { + val s = when (someBoolean) { + true || true -> 1 + false && false -> 2 + true -> 3 + false -> 4 + } +} + +fun test_2(someBoolean: Boolean) { + val s = when (someBoolean) { + true || true -> 1 + false && false -> 2 + } +} diff --git a/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.txt b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.txt new file mode 100644 index 00000000000..56a649504ba --- /dev/null +++ b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.txt @@ -0,0 +1,7 @@ +package + +public const val myF: kotlin.Boolean = false +public const val myT: kotlin.Boolean = true +public fun test(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit +public fun test_2(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit + diff --git a/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt new file mode 100644 index 00000000000..a31882a9592 --- /dev/null +++ b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt @@ -0,0 +1,21 @@ +// FIR_IDENTICAL +// LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions + +const val myF = false +const val myT = true + +fun test(someBoolean: Boolean) { + val s = when (someBoolean) { + true || true -> 1 + false && false -> 2 + true -> 3 + false -> 4 + } +} + +fun test_2(someBoolean: Boolean) { + val s = when (someBoolean) { + true || true -> 1 + false && false -> 2 + } +} diff --git a/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.txt b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.txt new file mode 100644 index 00000000000..56a649504ba --- /dev/null +++ b/compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.txt @@ -0,0 +1,7 @@ +package + +public const val myF: kotlin.Boolean = false +public const val myT: kotlin.Boolean = true +public fun test(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit +public fun test_2(/*0*/ someBoolean: kotlin.Boolean): kotlin.Unit + diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index ca12e1cb393..94671802e87 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -4948,6 +4948,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/divideByZero.kt"); } + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_disabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_disabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_disabled.kt"); + } + + @Test + @TestMetadata("duplicateLabelWithNonTrivialCondition_enabled.kt") + public void testDuplicateLabelWithNonTrivialCondition_enabled() throws Exception { + runTest("compiler/testData/diagnostics/tests/constantEvaluator/constant/duplicateLabelWithNonTrivialCondition_enabled.kt"); + } + @Test @TestMetadata("equals.kt") public void testEquals() throws Exception {