diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -568,6 +568,9 @@ - Clang now correctly evaluates ``__has_extension (cxx_defaulted_functions)`` and ``__has_extension (cxx_default_function_template_args)`` to 1. (`#61758 `_) +- Stop evaluating a constant expression if the condition expression which in + switch statement contains errors. + (`#63453 _`) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -4913,11 +4913,20 @@ static bool EvaluateDependentExpr(const Expr *E, EvalInfo &Info) { assert(E->isValueDependent()); - if (Info.noteSideEffect()) - return true; + // Note that we have a side effect that matters for constant evaluation. + bool SideEffects = Info.noteSideEffect(); + // If the reason we're here is because of a recovery expression, we don't + // want to continue to evaluate further as we will never know what the actual + // value is. + if (isa(E)) + return false; + + // Otherwise, return whether we want to continue after noting the side + // effects, which should only happen if the expression has errors but isn't + // a recovery expression on its own. assert(E->containsErrors() && "valid value-dependent expression should never " "reach invalid code path."); - return false; + return SideEffects; } /// Evaluate a condition (either a variable declaration or an expression). diff --git a/clang/test/SemaCXX/constexpr-function-recovery-crash.cpp b/clang/test/SemaCXX/constexpr-function-recovery-crash.cpp --- a/clang/test/SemaCXX/constexpr-function-recovery-crash.cpp +++ b/clang/test/SemaCXX/constexpr-function-recovery-crash.cpp @@ -78,6 +78,132 @@ constexpr int test12() { return "wrong"; } // expected-error {{cannot initialize return object of type 'int'}} constexpr int force12 = test12(); // expected-error {{must be initialized by a constant}} +constexpr int test13() { + switch (invalid_value) { // expected-error {{use of undeclared identifier}} + case 0: + return 7; + default: + break; + } + return 0; +} + +static_assert(test13(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test14() { + int sum = 0; + for (int i = 0; i < invalid_value; ++i) // expected-error {{use of undeclared identifier}} + sum = sum + i; + return sum; +} + +static_assert(test14(), "should not crash"); // expected-error {{static assertion failed due to requirement}} + +constexpr int test15() { + int sum = 0; + for (int i = 0; i < 10; i += invalid_value) // expected-error {{use of undeclared identifier}} + sum = sum + i; + return sum; +} + +static_assert(test15(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test16() { + int sum = 0; + for (int i = invalid_value; i < 10; ++i) // expected-error {{use of undeclared identifier}} + sum = sum + i; + return sum; +} + +static_assert(test16(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test17() { + int sum = 0, i = 0; + do + sum += i; + while (invalid_value); // expected-error {{use of undeclared identifier}} + return sum; +} + +static_assert(test17(), "should not crash"); // expected-error {{static assertion failed due to requirement}} + +constexpr int test18() { + int sum = 0, i = 0; + while (invalid_value) // expected-error {{use of undeclared identifier}} + sum += i; + return sum; +} + +static_assert(test18(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test19() { + struct Test19 { + int a[2] = {0, 1}; + constexpr const int *begin() const { + return invalid_value; // expected-error {{use of undeclared identifier}} + } + constexpr const int *end() const { + return a + 2; + } + }; + + int sum = 0; + Test19 t; + for (const auto v : t) { + sum += v; + } + return sum; +} + +static_assert(test19(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test20() { + struct Test20 { + int a[2] = {0, 1}; + constexpr const int *begin() const { + return a; + } + constexpr const int *end() const { + return invalid_value; // expected-error {{use of undeclared identifier}} + } + }; + + int sum = 0; + Test20 t; + for (const auto v : t) { + sum += v; + } + return sum; +} + +static_assert(test20(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test21() { + return invalid_value; // expected-error {{use of undeclared identifier}} +} + +static_assert(test21(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test22() { + struct Test22 { + int value = invalid_value; // expected-error {{use of undeclared identifier}} + }; + return Test22().value; +} + +static_assert(test22(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + +constexpr int test23() { + struct Test23 { + int value; + constexpr Test23(int v) : value(v) {} + constexpr Test23(int a, int b) : Test23(a * b * invalid_value) {} // expected-error {{use of undeclared identifier}} + }; + return Test23(1, 2).value; +} + +static_assert(test23(), "should not crash"); // expected-error {{static assertion expression is not an integral constant expression}} + #define TEST_EVALUATE(Name, X) \ constexpr int testEvaluate##Name() { \ X return 0; \