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 cf35a690643..58f618e0cd6 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 @@ -4973,6 +4973,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt"); } + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_error.kt") + public void testLoopWithNonTrivialBooleanConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.kt"); + } + + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_warning.kt") + public void testLoopWithNonTrivialBooleanConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.kt"); + } + @Test @TestMetadata("mainWithWarningOnUnusedParam.kt") public void testMainWithWarningOnUnusedParam() throws Exception { @@ -30965,6 +30977,18 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti runTest("compiler/testData/diagnostics/tests/when/ExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_error.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt"); + } + + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_warning.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt"); + } + @Test @TestMetadata("ExhaustiveBreakContinue.kt") public void testExhaustiveBreakContinue() 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 d02fbbc9ddd..87f4028b81b 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 @@ -4973,6 +4973,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt"); } + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_error.kt") + public void testLoopWithNonTrivialBooleanConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.kt"); + } + + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_warning.kt") + public void testLoopWithNonTrivialBooleanConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.kt"); + } + @Test @TestMetadata("mainWithWarningOnUnusedParam.kt") public void testMainWithWarningOnUnusedParam() throws Exception { @@ -30965,6 +30977,18 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac runTest("compiler/testData/diagnostics/tests/when/ExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_error.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt"); + } + + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_warning.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt"); + } + @Test @TestMetadata("ExhaustiveBreakContinue.kt") public void testExhaustiveBreakContinue() throws Exception { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 466fdfe2b36..876fa098883 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -1028,7 +1028,7 @@ public interface Errors { DiagnosticFactory0 UNSIGNED_LITERAL_WITHOUT_DECLARATIONS_ON_CLASSPATH = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 SIGNED_CONSTANT_CONVERTED_TO_UNSIGNED = DiagnosticFactory0.create(ERROR); DiagnosticFactory1 INTEGER_OPERATOR_RESOLVE_WILL_CHANGE = DiagnosticFactory1.create(WARNING); - + DiagnosticFactory1 NON_TRIVIAL_BOOLEAN_CONSTANT = DiagnosticFactory1.create(WARNING); // Casts and is-checks 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 7c0e09ac7db..0ee986e3840 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -439,6 +439,7 @@ public class DefaultErrorMessages { MAP.put(UNSIGNED_LITERAL_WITHOUT_DECLARATIONS_ON_CLASSPATH, "Type of the constant expression cannot be resolved. Please make sure you have the required dependencies for unsigned types in the classpath"); MAP.put(SIGNED_CONSTANT_CONVERTED_TO_UNSIGNED, "Conversion of signed constants to unsigned ones is prohibited"); MAP.put(INTEGER_OPERATOR_RESOLVE_WILL_CHANGE, "This expression will be resolved to {0} in further releases. Please add explicit convention call", RENDER_TYPE); + MAP.put(NON_TRIVIAL_BOOLEAN_CONSTANT, "Compiler won't reduce this expression to {0} in future. Please replace it with boolean literal", TO_STRING); MAP.put(RESERVED_SYNTAX_IN_CALLABLE_REFERENCE_LHS, "Left-hand side of callable reference matches expression syntax reserved for future releases"); 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 1e3a273f34c..d8243843b82 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 @@ -44,6 +44,7 @@ import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import org.jetbrains.kotlin.types.expressions.OperatorConventions 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 java.math.BigInteger @@ -387,12 +388,39 @@ private class ConstantExpressionEvaluatorVisitor( val compileTimeConstant = expression.accept(this, expectedType ?: TypeUtils.NO_EXPECTED_TYPE) if (compileTimeConstant != null) { + if (shouldSkipComplexBooleanValue(expression, compileTimeConstant)) { + return null + } trace.record(BindingContext.COMPILE_TIME_VALUE, expression, compileTimeConstant) return compileTimeConstant } 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 (languageVersionSettings.supportsFeature(LanguageFeature.ProhibitSimplificationOfNonTrivialConstBooleanExpressions)) { + return true + } else { + val parent = expression.parent + if ( + parent is KtWhenConditionWithExpression || + parent is KtContainerNode && (parent.parent is KtWhileExpression || parent.parent is KtDoWhileExpression) + ) { + trace.report(Errors.NON_TRIVIAL_BOOLEAN_CONSTANT.on(expression, constantValue.value as Boolean)) + } + return false + } + } + private val stringExpressionEvaluator = object : KtVisitor, Nothing?>() { private fun createStringConstant(compileTimeConstant: CompileTimeConstant<*>): TypedCompileTimeConstant? { val constantValue = compileTimeConstant.toConstantValue(TypeUtils.NO_EXPECTED_TYPE) diff --git a/compiler/testData/diagnostics/tests/annotations/onLoops.kt b/compiler/testData/diagnostics/tests/annotations/onLoops.kt index bf5acfe7d2d..6caf742f115 100644 --- a/compiler/testData/diagnostics/tests/annotations/onLoops.kt +++ b/compiler/testData/diagnostics/tests/annotations/onLoops.kt @@ -1,4 +1,5 @@ // FIR_IDENTICAL +// LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions fun test() { @ann while (2 < 1) {} @@ -10,4 +11,4 @@ fun test() { for (i in 1..2) {} } -annotation class ann \ No newline at end of file +annotation class ann diff --git a/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.fir.kt b/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.fir.kt index 2500c6f6f4c..b9f05d0f4b2 100644 --- a/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.fir.kt +++ b/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.fir.kt @@ -1,3 +1,4 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions fun test() { @ann while (2 > 1) {} @@ -9,4 +10,4 @@ fun test() { for (i in 1..2) {} } -annotation class ann \ No newline at end of file +annotation class ann diff --git a/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.kt b/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.kt index 460bccc8094..53c0f968efe 100644 --- a/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.kt +++ b/compiler/testData/diagnostics/tests/annotations/onLoopsUnreachable.kt @@ -1,12 +1,13 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions fun test() { @ann - while (2 > 1) {} + while (2 > 1) {} @ann - do {} while (2 > 1) + do {} while (2 > 1) @ann for (i in 1..2) {} } -annotation class ann \ No newline at end of file +annotation class ann diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt index 1ff5abf6112..f82af44ce90 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.fir.kt @@ -1,3 +1,4 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION fun t1() : Int{ diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.kt index d3019a67a22..33fcc184084 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeDifferentExamples.kt @@ -1,3 +1,4 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions // !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_EXPRESSION fun t1() : Int{ @@ -68,12 +69,12 @@ fun t5() : Int { return 1 2 } - while (1 > 2) + while (1 > 2) return 1 } fun t6() : Int { - while (1 > 2) { + while (1 > 2) { return 1 2 } @@ -81,7 +82,7 @@ fun t6() : Int { } fun t6break() : Int { - while (1 > 2) { + while (1 > 2) { break 2 } diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.fir.kt index 14d4aa846e8..691a3d2dd63 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.fir.kt @@ -1,3 +1,4 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions fun unreachable() {} fun a() { @@ -52,4 +53,4 @@ fun h(): Int { while (true) { if (true) return 12 } -} // should work \ No newline at end of file +} // should work diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt index 7e6d421d766..f464c0f1208 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/infiniteLoops.kt @@ -1,3 +1,4 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions fun unreachable() {} fun a() { @@ -13,11 +14,11 @@ fun b() { } fun c() { - do {} while (1 == 1) + do {} while (1 == 1) } fun d() { - while (2 == 2) {} + while (2 == 2) {} } fun use(arg: Any) = arg @@ -52,4 +53,4 @@ fun h(): Int { while (true) { if (true) return 12 } -} // should work \ No newline at end of file +} // should work diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.fir.kt new file mode 100644 index 00000000000..f2a5ba0db21 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.fir.kt @@ -0,0 +1,72 @@ +// LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions +// DIAGNOSTICS: -UNUSED_VARIABLE + +fun test_1() { + while (true) { + + } + val x = 1 +} + +fun test_2() { + while (true || false) { + + } + val x = 1 +} + +fun test_3() { + while (1 == 1) { + + } + val x = 1 +} + +fun test_4() { + while (false) { + val x = 1 + } + val y = 2 +} + +fun test_5() { + while (false && true) { + val x = 1 + } + val y = 2 +} + +fun test_6() { + do { + + } while (true) + val x = 1 +} + +fun test_7() { + do { + + } while (true || false) + val x = 1 +} + +fun test_8() { + do { + + } while (1 == 1) + val x = 1 +} + +fun test_9() { + do { + val x = 1 + } while (false) + val y = 2 +} + +fun test_10() { + do { + val x = 1 + } while (false && true) + val y = 2 +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.kt new file mode 100644 index 00000000000..e6763002fb0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.kt @@ -0,0 +1,73 @@ +// LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions +// DIAGNOSTICS: -UNUSED_VARIABLE + +fun test_1() { + while (true) { + + } + val x = 1 +} + +fun test_2() { + while (true || false) { + + } + val x = 1 +} + +fun test_3() { + while (1 == 1) { + + } + val x = 1 +} + +fun test_4() { + while (false) { + val x = 1 + } + val y = 2 +} + +fun test_5() { + while (false && true) { + val x = 1 + } + val y = 2 +} + +fun test_6() { + do { + + } while (true) + val x = 1 +} + +fun test_7() { + do { + + } while (true || false) + val x = 1 +} + +fun test_8() { + do { + + } while (1 == 1) + val x = 1 +} + +fun test_9() { + do { + val x = 1 + } while (false) + val y = 2 +} + +fun test_10() { + do { + val x = 1 + } while (false && true) + val y = 2 +} + diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.txt new file mode 100644 index 00000000000..e8e2bc6ce34 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.txt @@ -0,0 +1,13 @@ +package + +public fun test_1(): kotlin.Unit +public fun test_10(): kotlin.Unit +public fun test_2(): kotlin.Unit +public fun test_3(): kotlin.Unit +public fun test_4(): kotlin.Unit +public fun test_5(): kotlin.Unit +public fun test_6(): kotlin.Unit +public fun test_7(): kotlin.Unit +public fun test_8(): kotlin.Unit +public fun test_9(): kotlin.Unit + diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.fir.kt new file mode 100644 index 00000000000..1d693a13c57 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.fir.kt @@ -0,0 +1,72 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions +// DIAGNOSTICS: -UNUSED_VARIABLE + +fun test_1() { + while (true) { + + } + val x = 1 +} + +fun test_2() { + while (true || false) { + + } + val x = 1 +} + +fun test_3() { + while (1 == 1) { + + } + val x = 1 +} + +fun test_4() { + while (false) { + val x = 1 + } + val y = 2 +} + +fun test_5() { + while (false && true) { + val x = 1 + } + val y = 2 +} + +fun test_6() { + do { + + } while (true) + val x = 1 +} + +fun test_7() { + do { + + } while (true || false) + val x = 1 +} + +fun test_8() { + do { + + } while (1 == 1) + val x = 1 +} + +fun test_9() { + do { + val x = 1 + } while (false) + val y = 2 +} + +fun test_10() { + do { + val x = 1 + } while (false && true) + val y = 2 +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.kt new file mode 100644 index 00000000000..90653afc7e7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.kt @@ -0,0 +1,72 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions +// DIAGNOSTICS: -UNUSED_VARIABLE + +fun test_1() { + while (true) { + + } + val x = 1 +} + +fun test_2() { + while (true || false) { + + } + val x = 1 +} + +fun test_3() { + while (1 == 1) { + + } + val x = 1 +} + +fun test_4() { + while (false) { + val x = 1 + } + val y = 2 +} + +fun test_5() { + while (false && true) { + val x = 1 + } + val y = 2 +} + +fun test_6() { + do { + + } while (true) + val x = 1 +} + +fun test_7() { + do { + + } while (true || false) + val x = 1 +} + +fun test_8() { + do { + + } while (1 == 1) + val x = 1 +} + +fun test_9() { + do { + val x = 1 + } while (false) + val y = 2 +} + +fun test_10() { + do { + val x = 1 + } while (false && true) + val y = 2 +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.txt new file mode 100644 index 00000000000..e8e2bc6ce34 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.txt @@ -0,0 +1,13 @@ +package + +public fun test_1(): kotlin.Unit +public fun test_10(): kotlin.Unit +public fun test_2(): kotlin.Unit +public fun test_3(): kotlin.Unit +public fun test_4(): kotlin.Unit +public fun test_5(): kotlin.Unit +public fun test_6(): kotlin.Unit +public fun test_7(): kotlin.Unit +public fun test_8(): kotlin.Unit +public fun test_9(): kotlin.Unit + diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.fir.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.fir.kt new file mode 100644 index 00000000000..16083bf879b --- /dev/null +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.fir.kt @@ -0,0 +1,12 @@ +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions +fun x(): Boolean { return true } + +public fun foo(p: String?): Int { + // Like whileTrue but 2 == 2 is in use + while(2 == 2) { + p!!.length + if (x()) break + } + // Smart cast should not work in this case, see KT-6284 + return p.length +} diff --git a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt index 87ac7643d51..9f693b9663c 100644 --- a/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt +++ b/compiler/testData/diagnostics/tests/smartCasts/loops/whileTrivial.kt @@ -1,12 +1,12 @@ -// FIR_IDENTICAL +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions fun x(): Boolean { return true } public fun foo(p: String?): Int { // Like whileTrue but 2 == 2 is in use - while(2 == 2) { + while(2 == 2) { p!!.length if (x()) break } // Smart cast should not work in this case, see KT-6284 return p.length -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.fir.kt b/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.fir.kt deleted file mode 100644 index 298acefd490..00000000000 --- a/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE) - * - * SPEC VERSION: 0.1-313 - * PRIMARY LINKS: expressions, when-expression -> paragraph 5 -> sentence 1 - * expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 3 - * expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 4 - * expressions, when-expression, exhaustive-when-expressions -> paragraph 2 -> sentence 5 - */ - -// See also: KT-3743 -fun foo(arg: Boolean): String { - // Must be exhaustive - return when(arg) { - 2 == 2 -> "truth" - 2 == 1 -> "falsehood" - } -} diff --git a/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt b/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt index 11e7d5f8d1c..629427837f8 100644 --- a/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt +++ b/compiler/testData/diagnostics/tests/when/ExhaustiveBooleanComplex.kt @@ -1,3 +1,5 @@ +// FIR_IDENTICAL +// !LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions /* * KOTLIN DIAGNOSTICS SPEC TEST (NEGATIVE) * @@ -11,7 +13,7 @@ // See also: KT-3743 fun foo(arg: Boolean): String { // Must be exhaustive - return when(arg) { + return when(arg) { 2 == 2 -> "truth" 2 == 1 -> "falsehood" } diff --git a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt new file mode 100644 index 00000000000..1a72422b5e1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt @@ -0,0 +1,32 @@ +// FIR_IDENTICAL +// !LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions +// ISSUE: KT-39883 + +// Should always work +fun test_0(b: Boolean): String = when (b) { + true -> "true" + false -> "false" +} + +// Deprecated +fun test_1(b: Boolean): String = when (b) { + 1 == 1 -> "true" + "" != "" -> "false" +} + +const val TRUE = true + +// Already not working +fun test_2(b: Boolean): String = when(b) { + TRUE -> "true" + false -> "false" +} + +const val s1 = "s1" +const val s2 = "s2" + +// Already not working +fun test_3(b: Boolean): String = when(b) { + true -> "true" + s1 == s2 -> "false" +} diff --git a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.txt b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.txt new file mode 100644 index 00000000000..7be72a67afe --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.txt @@ -0,0 +1,9 @@ +package + +public const val TRUE: kotlin.Boolean = true +public const val s1: kotlin.String = "s1" +public const val s2: kotlin.String = "s2" +public fun test_0(/*0*/ b: kotlin.Boolean): kotlin.String +public fun test_1(/*0*/ b: kotlin.Boolean): kotlin.String +public fun test_2(/*0*/ b: kotlin.Boolean): kotlin.String +public fun test_3(/*0*/ b: kotlin.Boolean): kotlin.String diff --git a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.fir.kt b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.fir.kt new file mode 100644 index 00000000000..bfe1fc465a1 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.fir.kt @@ -0,0 +1,31 @@ +// !LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions +// ISSUE: KT-39883 + +// Should always work +fun test_0(b: Boolean): String = when (b) { + true -> "true" + false -> "false" +} + +// Deprecated +fun test_1(b: Boolean): String = when (b) { + 1 == 1 -> "true" + "" != "" -> "false" +} + +const val TRUE = true + +// Already not working +fun test_2(b: Boolean): String = when(b) { + TRUE -> "true" + false -> "false" +} + +const val s1 = "s1" +const val s2 = "s2" + +// Already not working +fun test_3(b: Boolean): String = when(b) { + true -> "true" + s1 == s2 -> "false" +} diff --git a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt new file mode 100644 index 00000000000..705d8576547 --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt @@ -0,0 +1,31 @@ +// !LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions +// ISSUE: KT-39883 + +// Should always work +fun test_0(b: Boolean): String = when (b) { + true -> "true" + false -> "false" +} + +// Deprecated +fun test_1(b: Boolean): String = when (b) { + 1 == 1 -> "true" + "" != "" -> "false" +} + +const val TRUE = true + +// Already not working +fun test_2(b: Boolean): String = when(b) { + TRUE -> "true" + false -> "false" +} + +const val s1 = "s1" +const val s2 = "s2" + +// Already not working +fun test_3(b: Boolean): String = when(b) { + true -> "true" + s1 == s2 -> "false" +} diff --git a/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.txt b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.txt new file mode 100644 index 00000000000..7be72a67afe --- /dev/null +++ b/compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.txt @@ -0,0 +1,9 @@ +package + +public const val TRUE: kotlin.Boolean = true +public const val s1: kotlin.String = "s1" +public const val s2: kotlin.String = "s2" +public fun test_0(/*0*/ b: kotlin.Boolean): kotlin.String +public fun test_1(/*0*/ b: kotlin.Boolean): kotlin.String +public fun test_2(/*0*/ b: kotlin.Boolean): kotlin.String +public fun test_3(/*0*/ b: kotlin.Boolean): kotlin.String 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 8b68ca3abbf..777ed93f94c 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 @@ -4979,6 +4979,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt"); } + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_error.kt") + public void testLoopWithNonTrivialBooleanConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.kt"); + } + + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_warning.kt") + public void testLoopWithNonTrivialBooleanConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.kt"); + } + @Test @TestMetadata("mainWithWarningOnUnusedParam.kt") public void testMainWithWarningOnUnusedParam() throws Exception { @@ -31061,6 +31073,18 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { runTest("compiler/testData/diagnostics/tests/when/ExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_error.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt"); + } + + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_warning.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt"); + } + @Test @TestMetadata("ExhaustiveBreakContinue.kt") public void testExhaustiveBreakContinue() throws Exception { diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt index 836b4f37b7d..b7266707c0d 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/11.1.kt @@ -1,4 +1,5 @@ // FIR_IDENTICAL +// LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions // !DIAGNOSTICS: -UNUSED_EXPRESSION // SKIP_TXT @@ -26,3 +27,10 @@ fun case_2(value_1: Boolean?): String = when(value_1) { // TESTCASE NUMBER: 3 fun case_3(value_1: Boolean?): Int = when(value_1) { } + +// TESTCASE NUMBER: 4 +fun case_4(value_1: Boolean?): String = when (value_1) { + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" + null -> "" +} diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt index ab7cdda9b7b..bf4cc306d2c 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/neg/3.1.kt @@ -1,4 +1,5 @@ // FIR_IDENTICAL +// LANGUAGE: +ProhibitSimplificationOfNonTrivialConstBooleanExpressions // !DIAGNOSTICS: -UNUSED_EXPRESSION // SKIP_TXT @@ -44,3 +45,10 @@ fun case_5(value_1: Boolean): String { falseValue -> "" } } + + +// TESTCASE NUMBER: 6 +fun case_6(value_1: Boolean): String = when (value_1) { + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" +} diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt index 073abe3ea85..8a6e7ef69f9 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/11.1.kt @@ -1,4 +1,5 @@ // SKIP_TXT +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions /* * KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE) @@ -18,7 +19,7 @@ fun case_1(value_1: Boolean?): String = when (value_1) { // TESTCASE NUMBER: 2 fun case_2(value_1: Boolean?): String = when (value_1) { - true && false && ((true || false)) || true && !!!false && !!!true -> "" - true && false && ((true || false)) || true && !!!false -> "" + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" null -> "" } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt index 41f07c616de..81158d23ad0 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/exhaustive-when-expressions/p-2/pos/3.1.kt @@ -1,4 +1,5 @@ // SKIP_TXT +// LANGUAGE: -ProhibitSimplificationOfNonTrivialConstBooleanExpressions /* * KOTLIN DIAGNOSTICS SPEC TEST (POSITIVE) @@ -17,6 +18,6 @@ fun case_1(value_1: Boolean): String = when (value_1) { // TESTCASE NUMBER: 2 fun case_2(value_1: Boolean): String = when (value_1) { - true && false && ((true || false)) || true && !!!false && !!!true -> "" - true && false && ((true || false)) || true && !!!false -> "" + true && false && ((true || false)) || true && !!!false && !!!true -> "" + true && false && ((true || false)) || true && !!!false -> "" } diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/neg/1.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/neg/1.1.kt index c1deb6459e0..4ca0901e7d8 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/neg/1.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/neg/1.1.kt @@ -1,4 +1,4 @@ -// !LANGUAGE: +NewInference +// !LANGUAGE: +NewInference +ProhibitSimplificationOfNonTrivialConstBooleanExpressions // !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION // SKIP_TXT // FULL_JDK @@ -66,4 +66,25 @@ sealed class SClass { class A : SClass() class B : SClass() class C : SClass() -} \ No newline at end of file +} + +// TESTCASE NUMBER: 5 + +fun case5() { + val b = false + val when1: Any = when (b) { + false -> { } + !false -> { } + else -> { } + } + + val when2: Any = when (b) { + false -> { } + !false -> { } + } + val when3: Any = when (b) { + false -> { } + false -> { } + !false -> { } + } +} diff --git a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/pos/1.1.kt b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/pos/1.1.kt index da6553398ef..f6f95a9a2d1 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/pos/1.1.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/expressions/when-expression/p-4/pos/1.1.kt @@ -1,4 +1,4 @@ -// !LANGUAGE: +NewInference +// !LANGUAGE: +NewInference -ProhibitSimplificationOfNonTrivialConstBooleanExpressions // !DIAGNOSTICS: -UNUSED_VARIABLE -ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE -UNUSED_VALUE -UNUSED_PARAMETER -UNUSED_EXPRESSION // SKIP_TXT // FULL_JDK @@ -47,18 +47,18 @@ fun case2() { val b = false val when1: Any = when (b) { false -> { } - !false -> { } + !false -> { } else -> { } } val when2: Any = when (b) { false -> { } - !false -> { } + !false -> { } } val when3: Any = when (b) { false -> { } false -> { } - !false -> { } + !false -> { } } } @@ -111,4 +111,4 @@ sealed class SClass { class A : SClass() class B : SClass() class C : SClass() -} \ No newline at end of file +} diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index 0658697c77b..27eff4a755c 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -212,6 +212,7 @@ enum class LanguageFeature( ProperTypeInferenceConstraintsProcessing(KOTLIN_1_6, kind = BUG_FIX), ClassTypeParameterAnnotations(KOTLIN_1_6), SafeCallsAreAlwaysNullable(KOTLIN_1_6), + ProhibitSimplificationOfNonTrivialConstBooleanExpressions(KOTLIN_1_6), // Temporarily disabled, see KT-27084/KT-22379 SoundSmartcastFromLoopConditionForLoopAssignedVariables(sinceVersion = null, kind = BUG_FIX), diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index a7cbd846057..0ffbe4f29fe 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -4973,6 +4973,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/localObjectInConstructor.kt"); } + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_error.kt") + public void testLoopWithNonTrivialBooleanConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_error.kt"); + } + + @Test + @TestMetadata("loopWithNonTrivialBooleanConst_warning.kt") + public void testLoopWithNonTrivialBooleanConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/controlFlowAnalysis/loopWithNonTrivialBooleanConst_warning.kt"); + } + @Test @TestMetadata("mainWithWarningOnUnusedParam.kt") public void testMainWithWarningOnUnusedParam() throws Exception { @@ -30965,6 +30977,18 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag runTest("compiler/testData/diagnostics/tests/when/ExhaustiveBooleanNullable.kt"); } + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_error.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_error() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_error.kt"); + } + + @Test + @TestMetadata("exhaustiveBooleanWhenWithUntrivialConst_warning.kt") + public void testExhaustiveBooleanWhenWithUntrivialConst_warning() throws Exception { + runTest("compiler/testData/diagnostics/tests/when/exhaustiveBooleanWhenWithUntrivialConst_warning.kt"); + } + @Test @TestMetadata("ExhaustiveBreakContinue.kt") public void testExhaustiveBreakContinue() throws Exception {