hazohelet created this revision. hazohelet added reviewers: aaron.ballman, rsmith, cor3ntin, erichkeane, tbaeder. Herald added a project: All. hazohelet requested review of this revision. Herald added projects: clang, libc++. Herald added a subscriber: libcxx-commits. Herald added a reviewer: libc++.
This patch makes clang diagnose extensive cases of `consteval if` and `is_constant_evaluated` usage that are tautologically true or false. This introduces a new `Sema::ExpressionEvaluationContext` kind that means the immediate appearance of `if consteval` or `is_constant_evaluated` are tautologically false(e.g. inside `if !consteval {}` block or non-constexpr-qualified function definition body) This patch also pushes new expression evaluation context when parsing the condition of `if constexpr` and initializer of variables so that Sema can be aware that the use of `consteval if` and `is_consteval` are tautologically true in `if constexpr` condition and `constexpr` variable initializers. BEFORE this patch, the warning for `is_constant_evaluated` was emitted from constant evaluator. This patch moves the warning logic to Sema in order to diagnose tautological use of `is_constant_evaluated` in the same way as `consteval if`. Fixes https://github.com/llvm/llvm-project/issues/43760 Fixes https://github.com/llvm/llvm-project/issues/51567 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D155064 Files: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticASTKinds.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Sema.h clang/lib/AST/ExprConstant.cpp clang/lib/Parse/ParseCXXInlineMethods.cpp clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseDeclCXX.cpp clang/lib/Parse/ParseExpr.cpp clang/lib/Parse/ParseStmt.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaDeclCXX.cpp clang/lib/Sema/SemaExpr.cpp clang/lib/Sema/SemaExprMember.cpp clang/lib/Sema/SemaLambda.cpp clang/lib/Sema/SemaStmt.cpp clang/test/AST/Interp/builtins.cpp clang/test/AST/Interp/if.cpp clang/test/AST/Interp/literals.cpp clang/test/CXX/expr/expr.const/p2-0x.cpp clang/test/CXX/expr/expr.const/p6-2a.cpp clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp clang/test/Parser/pragma-fenv_access.c clang/test/SemaCXX/constant-expression-cxx11.cpp clang/test/SemaCXX/cxx2a-consteval.cpp clang/test/SemaCXX/cxx2b-consteval-if.cpp clang/test/SemaCXX/ext-int.cpp clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp clang/test/SemaCXX/warn-tautological-meta-constant.cpp clang/test/SemaTemplate/concepts.cpp clang/unittests/Support/TimeProfilerTest.cpp libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp
Index: libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp =================================================================== --- libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp +++ libcxx/test/std/utilities/meta/meta.const.eval/is_constant_evaluated.verify.cpp @@ -24,7 +24,7 @@ #else // expected-error-re@+1 {{{{(static_assert|static assertion)}} failed}} static_assert(!std::is_constant_evaluated(), ""); - // expected-warning@-1 0-1 {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + // expected-warning@-1 0-1 {{'std::is_constant_evaluated' will always evaluate to true in this context}} #endif return 0; } Index: libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp =================================================================== --- libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp +++ libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp @@ -93,12 +93,10 @@ } int main(int, char**) { - if (!std::is_constant_evaluated()) { - test_containers<std::deque<int>, std::deque<int>>(); - test_containers<std::deque<int>, std::vector<int>>(); - test_containers<std::vector<int>, std::deque<int>>(); - test_containers<std::vector<int>, std::vector<int>>(); - } + test_containers<std::deque<int>, std::deque<int>>(); + test_containers<std::deque<int>, std::vector<int>>(); + test_containers<std::vector<int>, std::deque<int>>(); + test_containers<std::vector<int>, std::vector<int>>(); types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> { test_join_view<Iter, Iter>(); Index: clang/unittests/Support/TimeProfilerTest.cpp =================================================================== --- clang/unittests/Support/TimeProfilerTest.cpp +++ clang/unittests/Support/TimeProfilerTest.cpp @@ -188,7 +188,6 @@ | EvaluateAsBooleanCondition (<test.cc:8:21, col:25>) | | EvaluateAsRValue (<test.cc:8:21, col:25>) | EvaluateAsInitializer (slow_value) -| EvaluateAsConstantExpr (<test.cc:17:33, col:59>) | EvaluateAsConstantExpr (<test.cc:18:11, col:37>) | EvaluateAsRValue (<test.cc:22:14, line:23:58>) | EvaluateAsInitializer (slow_init_list) Index: clang/test/SemaTemplate/concepts.cpp =================================================================== --- clang/test/SemaTemplate/concepts.cpp +++ clang/test/SemaTemplate/concepts.cpp @@ -135,21 +135,21 @@ namespace BuiltinIsConstantEvaluated { // Check that we do all satisfaction and diagnostic checks in a constant context. - template<typename T> concept C = __builtin_is_constant_evaluated(); // expected-warning {{always}} + template<typename T> concept C = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} static_assert(C<int>); - template<typename T> concept D = __builtin_is_constant_evaluated() == true; // expected-warning {{always}} + template<typename T> concept D = __builtin_is_constant_evaluated() == true; // expected-warning {{always evaluate to true}} static_assert(D<int>); - template<typename T> concept E = __builtin_is_constant_evaluated() == true && // expected-warning {{always}} + template<typename T> concept E = __builtin_is_constant_evaluated() == true && // expected-warning {{always evaluate to true}} false; // expected-note {{'false' evaluated to false}} static_assert(E<int>); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'E'}} - template<typename T> concept F = __builtin_is_constant_evaluated() == false; // expected-warning {{always}} + template<typename T> concept F = __builtin_is_constant_evaluated() == false; // expected-warning {{always evaluate to true}} // expected-note@-1 {{'__builtin_is_constant_evaluated() == false' (1 == 0)}} static_assert(F<int>); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'F'}} - template<typename T> concept G = __builtin_is_constant_evaluated() && // expected-warning {{always}} + template<typename T> concept G = __builtin_is_constant_evaluated() && // expected-warning {{always evaluate to true}} false; // expected-note {{'false' evaluated to false}} static_assert(G<int>); // expected-error {{failed}} expected-note {{because 'int' does not satisfy 'G'}} } Index: clang/test/SemaCXX/warn-tautological-meta-constant.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/warn-tautological-meta-constant.cpp @@ -0,0 +1,170 @@ +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s + +namespace std { +constexpr inline bool + is_constant_evaluated() noexcept { + if consteval { return true; } else { return false; } + } +} // namespace std + +namespace P1938 { + constexpr int f1() { + if constexpr (!std::is_constant_evaluated() && sizeof(int) == 4) { // expected-warning {{always evaluate to true}} + return 0; + } + if (std::is_constant_evaluated()) { + return 42; + } else { + if constexpr (std::is_constant_evaluated()) { // expected-warning {{always evaluate to true}} + return 0; + } + } + return 7; +} + + +consteval int f2() { + if (std::is_constant_evaluated() && f1()) { // expected-warning {{always evaluate to true}} + return 42; + } + return 7; +} + + +int f3() { + if (std::is_constant_evaluated() && f1()) { // expected-warning {{always evaluate to false}} + return 42; + } + return 7; +} +} + +void non_qual() { + int ff = std::is_constant_evaluated(); // expected-warning {{always evaluate to false}} + const int aa = std::is_constant_evaluated(); + constexpr int tt = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + static int bb = std::is_constant_evaluated(); + constexpr int cc = [](){ + if consteval {return 8;} // expected-warning {{always true}} + }(); + auto lamda = []() { + if consteval {return 8;} + else {return 4;} + }; + auto lamda_const = []() consteval { + if consteval {return 8;} // expected-warning {{always true}} + else {return 4;} + }; + if consteval { // expected-warning {{always false}} + int b = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } +} + +constexpr void in_constexpr() { + int aa = std::is_constant_evaluated(); + constexpr int bb = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int cc = std::is_constant_evaluated(); + if consteval { + int dd = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + constexpr int ee = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int ff = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } else { + int dd = std::is_constant_evaluated(); // expected-warning {{always evaluate to false}} + constexpr int ee = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int ff = std::is_constant_evaluated(); + const int qq = std::is_constant_evaluated() ? dd : 3; + } + + if consteval { + if consteval {} // expected-warning {{always true}} + if !consteval {} // expected-warning {{always false}} + } else { + if consteval {} // expected-warning {{always false}} + if !consteval {} // expected-warning {{always true}} + } + if !consteval { + if consteval {} // expected-warning {{always false}} + if !consteval {} // expected-warning {{always true}} + } else { + if consteval {} // expected-warning {{always true}} + if !consteval {} // expected-warning {{always false}} + } +} + +consteval void in_consteval() { + int aa = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + constexpr int bb = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + auto lambda = []() { + int a(std::is_constant_evaluated()); // expected-warning {{always evaluate to true}} + constexpr int b = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const int c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + }; + if !consteval {} // expected-warning {{always false}} +} + +static_assert(std::is_constant_evaluated()); // expected-warning {{always evaluate to true}} +static_assert(__builtin_is_constant_evaluated()); // expected-warning {{always evaluate to true}} + +template <bool b> +void templ() { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + constexpr bool c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + if consteval {} // expected-warning {{always false}} +} + +template <> void templ<std::is_constant_evaluated()>() { // expected-warning {{always evaluate to true}} + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + constexpr bool c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + if consteval {} // expected-warning {{always false}} + templ<false>(); +} + +static_assert([] { + if consteval { // expected-warning {{always true}} + return 0; + } else { + return 1; + } + }() == 0); +constexpr bool b = __builtin_is_constant_evaluated(); // expected-warning {{always evaluate to true}} +constexpr bool c = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} +constinit bool d = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} +int p = __builtin_is_constant_evaluated(); +const int q = __builtin_is_constant_evaluated(); + +template <bool c = std::is_constant_evaluated()> // expected-warning {{always evaluate to true}} +void vvv() { + return; +} + +template<> void vvv<true>() {} +template<> void vvv<false>() {} + +template<typename T> concept C = __builtin_is_constant_evaluated();// expected-warning {{always evaluate to true}} + +struct Foo { + static constexpr bool ce = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + const static bool nonce = std::is_constant_evaluated(); + bool b = std::is_constant_evaluated(); + + Foo() { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + bool aa = std::is_constant_evaluated(); // expected-warning {{always evaluate to false}} + static bool bb = std::is_constant_evaluated(); + constexpr bool cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + if consteval {} // expected-warning {{always false}} + } + constexpr Foo(int) { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + bool aa = std::is_constant_evaluated(); + static bool bb = std::is_constant_evaluated(); + constexpr bool cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } + consteval Foo(int *) { + if constexpr(std::is_constant_evaluated()) {} // expected-warning {{always evaluate to true}} + bool aa = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + static bool bb = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + constexpr bool cc = std::is_constant_evaluated(); // expected-warning {{always evaluate to true}} + } +}; Index: clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp =================================================================== --- clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp +++ clang/test/SemaCXX/warn-constant-evaluated-constexpr.cpp @@ -7,35 +7,35 @@ } // namespace std constexpr int fn1() { - if constexpr (std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn2() { - if constexpr (!std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (!std::is_constant_evaluated()) // expected-warning {{'std::is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn3() { - if constexpr (std::is_constant_evaluated() == false) // expected-warning {{'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (std::is_constant_evaluated() == false) // expected-warning {{'std::is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn4() { - if constexpr (__builtin_is_constant_evaluated() == true) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (__builtin_is_constant_evaluated() == true) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; } constexpr int fn5() { - if constexpr (__builtin_is_constant_evaluated()) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression}} + if constexpr (__builtin_is_constant_evaluated()) // expected-warning {{'__builtin_is_constant_evaluated' will always evaluate to true in this context}} return 0; else return 1; Index: clang/test/SemaCXX/ext-int.cpp =================================================================== --- clang/test/SemaCXX/ext-int.cpp +++ clang/test/SemaCXX/ext-int.cpp @@ -25,7 +25,7 @@ unsigned _BitInt(1) l; signed _BitInt(1) m; // expected-error{{signed _BitInt must have a bit size of at least 2}} - constexpr _BitInt(6) n = 33; // expected-warning{{implicit conversion from 'int' to 'const _BitInt(6)' changes value from 33 to -31}} + constexpr _BitInt(6) n = 33; constexpr _BitInt(7) o = 33; // Check imposed max size. Index: clang/test/SemaCXX/cxx2b-consteval-if.cpp =================================================================== --- clang/test/SemaCXX/cxx2b-consteval-if.cpp +++ clang/test/SemaCXX/cxx2b-consteval-if.cpp @@ -18,7 +18,7 @@ constexpr auto i() { if consteval { - if consteval { // expected-warning {{consteval if is always true in an immediate context}} + if consteval { // expected-warning {{consteval if is always true in this context}} return 1; } return 2; Index: clang/test/SemaCXX/cxx2a-consteval.cpp =================================================================== --- clang/test/SemaCXX/cxx2a-consteval.cpp +++ clang/test/SemaCXX/cxx2a-consteval.cpp @@ -713,7 +713,7 @@ struct test { consteval int operator[](int i) const { return {}; } consteval const derp * operator->() const { return &d; } - consteval int f() const { return 12; } // expected-note 2{{declared here}} + consteval int f() const { return 12; } // expected-note {{declared here}} }; constexpr test a; @@ -726,8 +726,7 @@ constexpr int t = a[1]; constexpr int u = a.operator->()->b; constexpr int v = a->b; -// FIXME: I believe this case should work, but we currently reject. -constexpr int w = (a.*&test::f)(); // expected-error {{cannot take address of consteval function 'f' outside of an immediate invocation}} +constexpr int w = (a.*&test::f)(); constexpr int x = a.f(); // Show that we reject when not in an immediate context. @@ -1073,18 +1072,17 @@ consteval const char* make_name(const char* name) { return name;} consteval const char* pad(int P) { return "thestring"; } -int bad = 10; // expected-note 6{{declared here}} +int bad = 10; // expected-note 5{{declared here}} tester glob1(make_name("glob1")); tester glob2(make_name("glob2")); constexpr tester cglob(make_name("cglob")); -tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ +tester paddedglob(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::tester::tester' is not a constant expression}} \ // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} constexpr tester glob3 = { make_name("glob3") }; -constexpr tester glob4 = { make_name(pad(bad)) }; // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ - // expected-error {{constexpr variable 'glob4' must be initialized by a constant expression}} \ - // expected-note 2{{read of non-const variable 'bad' is not allowed in a constant expression}} +constexpr tester glob4 = { make_name(pad(bad)) }; // expected-error {{constexpr variable 'glob4' must be initialized by a constant expression}} \ + // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} auto V = make_name(pad(3)); auto V1 = make_name(pad(bad)); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ @@ -1094,12 +1092,12 @@ void foo() { static tester loc1(make_name("loc1")); static constexpr tester loc2(make_name("loc2")); - static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::tester::tester' is not a constant expression}} \ // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} } void bar() { - static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::make_name' is not a constant expression}} \ + static tester paddedloc(make_name(pad(bad))); // expected-error {{call to consteval function 'GH58207::tester::tester' is not a constant expression}} \ // expected-note {{read of non-const variable 'bad' is not allowed in a constant expression}} } } Index: clang/test/SemaCXX/constant-expression-cxx11.cpp =================================================================== --- clang/test/SemaCXX/constant-expression-cxx11.cpp +++ clang/test/SemaCXX/constant-expression-cxx11.cpp @@ -1961,7 +1961,7 @@ namespace Lifetime { void f() { - constexpr int &n = n; // expected-error {{constant expression}} expected-note {{use of reference outside its lifetime}} expected-warning {{not yet bound to a value}} + constexpr int &n = n; // expected-error {{constant expression}} expected-note {{use of reference outside its lifetime}} constexpr int m = m; // expected-error {{constant expression}} expected-note {{read of object outside its lifetime}} } Index: clang/test/Parser/pragma-fenv_access.c =================================================================== --- clang/test/Parser/pragma-fenv_access.c +++ clang/test/Parser/pragma-fenv_access.c @@ -33,7 +33,7 @@ CONST float fnot_too_big = not_too_big; CONST int too_big = 0x7ffffff0; #if defined(CPP) -//expected-warning@+2{{implicit conversion}} +// FIXME: should diagnose the narrowing happening below #endif CONST float fbig = too_big; // inexact #if !defined(CPP) Index: clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp =================================================================== --- clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp +++ clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp @@ -8,14 +8,14 @@ } else (void)0; // expected-error {{expected { after else}} static_assert([] { - if consteval { + if consteval { // expected-warning {{consteval if is always true}} return 0; } return 1; }() == 0); static_assert([] { - if consteval { + if consteval { // expected-warning {{consteval if is always true}} return 0; } else { return 1; @@ -23,7 +23,7 @@ }() == 0); static_assert([] { - if !consteval { + if !consteval { // expected-warning {{consteval if is always false}} return 0; } else { return 1; @@ -31,14 +31,15 @@ }() == 1); static_assert([] { - if not consteval { + if not consteval { // expected-warning {{consteval if is always false}} return 0; } return 1; }() == 1); if consteval [[likely]] { // expected-warning {{attribute 'likely' has no effect when annotating an 'if consteval' statement}}\ - // expected-note 2{{annotating the 'if consteval' statement here}} + // expected-note 2{{annotating the 'if consteval' statement here}} \ + // expected-warning {{consteval if is always false}} } @@ -49,7 +50,8 @@ } void test_consteval_jumps() { - if consteval { // expected-note 4{{jump enters controlled statement of consteval if}} + if consteval { // expected-warning {{consteval if is always false}} \ + // expected-note 4{{jump enters controlled statement of consteval if}} goto a; goto b; // expected-error {{cannot jump from this goto statement to its label}} a:; @@ -65,14 +67,16 @@ void test_consteval_switch() { int x = 42; switch (x) { - if consteval { // expected-note 2{{jump enters controlled statement of consteval if}} + if consteval { // expected-warning {{consteval if is always false}} \ + // expected-note 2{{jump enters controlled statement of consteval if}} case 1:; // expected-error {{cannot jump from switch statement to this case label}} default:; // expected-error {{cannot jump from switch statement to this case label}} } else { } } switch (x) { - if consteval { // expected-note 2{{jump enters controlled statement of consteval if}} + if consteval { // expected-warning {{consteval if is always false}} \ + // expected-note 2{{jump enters controlled statement of consteval if}} } else { case 2:; // expected-error {{cannot jump from switch statement to this case label}} default:; // expected-error {{cannot jump from switch statement to this case label}} @@ -99,32 +103,32 @@ } consteval void warn_in_consteval() { - if consteval { // expected-warning {{consteval if is always true in an immediate context}} - if consteval {} // expected-warning {{consteval if is always true in an immediate context}} + if consteval { // expected-warning {{consteval if is always true in this context}} + if consteval {} // expected-warning {{consteval if is always true in this context}} } } constexpr void warn_in_consteval2() { if consteval { - if consteval {} // expected-warning {{consteval if is always true in an immediate context}} + if consteval {} // expected-warning {{consteval if is always true in this context}} } } auto y = []() consteval { - if consteval { // expected-warning {{consteval if is always true in an immediate context}} - if consteval {} // expected-warning {{consteval if is always true in an immediate context}} + if consteval { // expected-warning {{consteval if is always true in this context}} + if consteval {} // expected-warning {{consteval if is always true in this context}} } }; namespace test_transform { int f(auto n) { - if consteval { + if consteval { // expected-warning {{consteval if is always false}} n.foo; //expected-error {{no member named}} } else { } - if !consteval { + if !consteval { // expected-warning {{consteval if is always true}} n.foo; //expected-error {{no member named}} } else { Index: clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp =================================================================== --- clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp +++ clang/test/CXX/expr/expr.prim/expr.prim.lambda/p3.cpp @@ -16,4 +16,5 @@ #if __cplusplus < 201703L // expected-error@-2 {{constexpr variable cannot have non-literal type}} // expected-note@-3 {{lambda closure types are non-literal types before C++17}} +// expected-error@-4 {{a lambda expression may not appear inside of a constant expression}} #endif Index: clang/test/CXX/expr/expr.const/p6-2a.cpp =================================================================== --- clang/test/CXX/expr/expr.const/p6-2a.cpp +++ clang/test/CXX/expr/expr.const/p6-2a.cpp @@ -43,12 +43,11 @@ constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}} namespace P1073R3 { -consteval int f() { return 42; } // expected-note 2 {{declared here}} +consteval int f() { return 42; } // expected-note {{declared here}} consteval auto g() { return f; } consteval int h(int (*p)() = g()) { return p(); } constexpr int r = h(); -constexpr auto e = g(); // expected-error {{call to consteval function 'P1073R3::g' is not a constant expression}} \ - expected-error {{constexpr variable 'e' must be initialized by a constant expression}} \ - expected-note 2 {{pointer to a consteval declaration is not a constant expression}} +constexpr auto e = g(); // expected-error {{constexpr variable 'e' must be initialized by a constant expression}} \ + expected-note {{pointer to a consteval declaration is not a constant expression}} static_assert(r == 42); } // namespace P1073R3 Index: clang/test/CXX/expr/expr.const/p2-0x.cpp =================================================================== --- clang/test/CXX/expr/expr.const/p2-0x.cpp +++ clang/test/CXX/expr/expr.const/p2-0x.cpp @@ -244,8 +244,8 @@ constexpr int n13 = n5 + n5; // expected-error {{constant expression}} expected-note {{value -4294967296 is outside the range of }} constexpr int n14 = n3 - n5; // expected-error {{constant expression}} expected-note {{value 4294967295 is outside the range of }} constexpr int n15 = n5 * n5; // expected-error {{constant expression}} expected-note {{value 4611686018427387904 is outside the range of }} - constexpr signed char c1 = 100 * 2; // ok expected-warning{{changes value}} - constexpr signed char c2 = '\x64' * '\2'; // also ok expected-warning{{changes value}} + constexpr signed char c1 = 100 * 2; // ok + constexpr signed char c2 = '\x64' * '\2'; // also ok constexpr long long ll1 = 0x7fffffffffffffff; // ok constexpr long long ll2 = ll1 + 1; // expected-error {{constant}} expected-note {{ 9223372036854775808 }} constexpr long long ll3 = -ll1 - 1; // ok Index: clang/test/AST/Interp/literals.cpp =================================================================== --- clang/test/AST/Interp/literals.cpp +++ clang/test/AST/Interp/literals.cpp @@ -169,15 +169,11 @@ #if __cplusplus >= 202002L /// FIXME: The following code should be accepted. consteval int foo(int n) { // ref-error {{consteval function never produces a constant expression}} - return sizeof(int[n]); // ref-note 3{{not valid in a constant expression}} \ - // expected-note {{not valid in a constant expression}} + return sizeof(int[n]); // ref-note 2 {{not valid in a constant expression}} } - constinit int var = foo(5); // ref-error {{not a constant expression}} \ - // ref-note 2{{in call to}} \ + constinit int var = foo(5); // ref-note {{in call to}} \ // ref-error {{does not have a constant initializer}} \ // ref-note {{required by 'constinit' specifier}} \ - // expected-error {{is not a constant expression}} \ - // expected-note {{in call to}} \ // expected-error {{does not have a constant initializer}} \ // expected-note {{required by 'constinit' specifier}} \ Index: clang/test/AST/Interp/if.cpp =================================================================== --- clang/test/AST/Interp/if.cpp +++ clang/test/AST/Interp/if.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fexperimental-new-constant-interpreter %s -verify -// RUN: %clang_cc1 -std=c++23 -fsyntax-only %s -verify=ref +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -fexperimental-new-constant-interpreter -Wno-redundant-consteval-if %s -verify +// RUN: %clang_cc1 -std=c++23 -fsyntax-only -Wno-redundant-consteval-if %s -verify=ref // expected-no-diagnostics // ref-no-diagnostics Index: clang/test/AST/Interp/builtins.cpp =================================================================== --- clang/test/AST/Interp/builtins.cpp +++ clang/test/AST/Interp/builtins.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -verify -// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -S -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -Wno-constant-evaluated -verify +// RUN: %clang_cc1 -fexperimental-new-constant-interpreter %s -Wno-constant-evaluated -S -emit-llvm -o - | FileCheck %s // RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated // RUN: %clang_cc1 -verify=ref %s -Wno-constant-evaluated %s -S -emit-llvm -o - | FileCheck %s Index: clang/lib/Sema/SemaStmt.cpp =================================================================== --- clang/lib/Sema/SemaStmt.cpp +++ clang/lib/Sema/SemaStmt.cpp @@ -933,16 +933,18 @@ } if (ConstevalOrNegatedConsteval) { - bool Immediate = ExprEvalContexts.back().Context == - ExpressionEvaluationContext::ImmediateFunctionContext; - if (CurContext->isFunctionOrMethod()) { - const auto *FD = - dyn_cast<FunctionDecl>(Decl::castFromDeclContext(CurContext)); - if (FD && FD->isImmediateFunction()) - Immediate = true; - } - if (isUnevaluatedContext() || Immediate) - Diags.Report(IfLoc, diag::warn_consteval_if_always_true) << Immediate; + bool AlwaysTrue = + ExprEvalContexts.back().Context == + ExpressionEvaluationContext::ImmediateFunctionContext || + ExprEvalContexts.back().InConstantEvaluated; + bool AlwaysFalse = ExprEvalContexts.back().Context == + ExpressionEvaluationContext::RuntimeEvaluated; + if (AlwaysTrue) + Diags.Report(IfLoc, diag::warn_consteval_if_always_true) + << (StatementKind == IfStatementKind::ConstevalNegated); + else if (AlwaysFalse) + Diags.Report(IfLoc, diag::warn_consteval_if_always_true) + << (StatementKind == IfStatementKind::ConstevalNonNegated); } return BuildIfStmt(IfLoc, StatementKind, LParenLoc, InitStmt, Cond, RParenLoc, Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -2141,6 +2141,7 @@ case ExpressionEvaluationContext::DiscardedStatement: case ExpressionEvaluationContext::PotentiallyEvaluated: + case ExpressionEvaluationContext::RuntimeEvaluated: case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: break; } Index: clang/lib/Sema/SemaExprMember.cpp =================================================================== --- clang/lib/Sema/SemaExprMember.cpp +++ clang/lib/Sema/SemaExprMember.cpp @@ -145,6 +145,7 @@ case Sema::ExpressionEvaluationContext::DiscardedStatement: case Sema::ExpressionEvaluationContext::ConstantEvaluated: case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: + case Sema::ExpressionEvaluationContext::RuntimeEvaluated: case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: break; Index: clang/lib/Sema/SemaExpr.cpp =================================================================== --- clang/lib/Sema/SemaExpr.cpp +++ clang/lib/Sema/SemaExpr.cpp @@ -7035,6 +7035,35 @@ << FixItHint::CreateInsertion(DRE->getLocation(), "std::"); } +// Diagnose uses of std::is_constant_evaluated or +// __builtin_is_constant_evaluated in contexts where the result is known at +// compile time. +static void DiagnoseTautologicalIsConstantEvaluated(Sema &S, CallExpr *CE) { + if (S.inTemplateInstantiation()) + return; + if (const FunctionDecl *FD = CE->getDirectCallee()) { + bool IsBuiltin = + FD->getBuiltinID() == Builtin::BI__builtin_is_constant_evaluated; + + if ((FD->isInStdNamespace() && + FD->getNameAsString() == "is_constant_evaluated") || + IsBuiltin) { + bool AlwaysTrue = + S.ExprEvalContexts.back().Context == + Sema::ExpressionEvaluationContext::ConstantEvaluated || + S.ExprEvalContexts.back().Context == + Sema::ExpressionEvaluationContext::ImmediateFunctionContext || + S.ExprEvalContexts.back().InConstantEvaluated; + bool AlwaysFalse = S.ExprEvalContexts.back().Context == + Sema::ExpressionEvaluationContext::RuntimeEvaluated; + if (AlwaysTrue || AlwaysFalse) + S.Diag(CE->getBeginLoc(), + diag::warn_is_constant_evaluated_tauto_constexpr) + << IsBuiltin << AlwaysTrue; + } + } +} + ExprResult Sema::ActOnCallExpr(Scope *Scope, Expr *Fn, SourceLocation LParenLoc, MultiExprArg ArgExprs, SourceLocation RParenLoc, Expr *ExecConfig) { @@ -7061,8 +7090,10 @@ ExecConfig); if (LangOpts.CPlusPlus) { CallExpr *CE = dyn_cast<CallExpr>(Call.get()); - if (CE) + if (CE) { DiagnosedUnqualifiedCallsToStdFunctions(*this, CE); + DiagnoseTautologicalIsConstantEvaluated(*this, CE); + } } return Call; } @@ -18067,6 +18098,10 @@ ExprEvalContexts.back().InImmediateFunctionContext = Prev.isImmediateFunctionContext() || Prev.isConstantEvaluated(); + ExprEvalContexts.back().InConstantEvaluated = + Prev.InConstantEvaluated || + ExprEvalContexts.back().InImmediateFunctionContext; + ExprEvalContexts.back().InImmediateEscalatingFunctionContext = Prev.InImmediateEscalatingFunctionContext; @@ -18436,7 +18471,8 @@ Rec.Context == Sema::ExpressionEvaluationContext::PotentiallyEvaluated || Rec.Context == - Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed; + Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed || + Rec.Context == Sema::ExpressionEvaluationContext::RuntimeEvaluated; if (SemaRef.inTemplateInstantiation() && IsPotentiallyEvaluated) ImmediateEscalating = Rec.InImmediateEscalatingFunctionContext; @@ -18551,6 +18587,7 @@ // -- a manifestly constant-evaluated expression, case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: + case Sema::ExpressionEvaluationContext::RuntimeEvaluated: case Sema::ExpressionEvaluationContext::DiscardedStatement: // -- a potentially-evaluated expression, case Sema::ExpressionEvaluationContext::UnevaluatedList: @@ -18672,6 +18709,7 @@ case Sema::ExpressionEvaluationContext::ConstantEvaluated: case Sema::ExpressionEvaluationContext::ImmediateFunctionContext: case Sema::ExpressionEvaluationContext::PotentiallyEvaluated: + case Sema::ExpressionEvaluationContext::RuntimeEvaluated: Result = OdrUseContext::Used; break; @@ -20763,6 +20801,7 @@ break; case ExpressionEvaluationContext::PotentiallyEvaluated: + case ExpressionEvaluationContext::RuntimeEvaluated: case ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed: return DiagIfReachable(Loc, Stmts, PD); } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -17959,15 +17959,6 @@ Diag(D->getLocation(), diag::err_illegal_initializer); } -/// Determine whether the given declaration is a global variable or -/// static data member. -static bool isNonlocalVariable(const Decl *D) { - if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(D)) - return Var->hasGlobalStorage(); - - return false; -} - /// Invoked when we are about to parse an initializer for the declaration /// 'Dcl'. /// @@ -17990,9 +17981,6 @@ // If we are parsing the initializer for a static data member, push a // new expression evaluation context that is associated with this static // data member. - if (isNonlocalVariable(D)) - PushExpressionEvaluationContext( - ExpressionEvaluationContext::PotentiallyEvaluated, D); } /// Invoked after we are finished parsing an initializer for the declaration D. @@ -18001,9 +17989,6 @@ if (!D || D->isInvalidDecl()) return; - if (isNonlocalVariable(D)) - PopExpressionEvaluationContext(); - if (S && D->isOutOfLine()) ExitDeclaratorContext(S); } Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -15238,14 +15238,18 @@ // Do not push if it is a lambda because one is already pushed when building // the lambda in ActOnStartOfLambdaDefinition(). - if (!isLambdaCallOperator(FD)) + if (!isLambdaCallOperator(FD)) { // [expr.const]/p14.1 // An expression or conversion is in an immediate function context if it is // potentially evaluated and either: its innermost enclosing non-block scope // is a function parameter scope of an immediate function. + PushExpressionEvaluationContext( - FD->isConsteval() ? ExpressionEvaluationContext::ImmediateFunctionContext - : ExprEvalContexts.back().Context); + FD->isConsteval() + ? ExpressionEvaluationContext::ImmediateFunctionContext + : FD->isConstexpr() ? ExpressionEvaluationContext::PotentiallyEvaluated + : ExpressionEvaluationContext::RuntimeEvaluated); + } // Each ExpressionEvaluationContextRecord also keeps track of whether the // context is nested in an immediate function context, so smaller contexts Index: clang/lib/Parse/ParseStmt.cpp =================================================================== --- clang/lib/Parse/ParseStmt.cpp +++ clang/lib/Parse/ParseStmt.cpp @@ -1510,6 +1510,10 @@ SourceLocation RParen; std::optional<bool> ConstexprCondition; if (!IsConsteval) { + EnterExpressionEvaluationContext Consteval( + Actions, Sema::ExpressionEvaluationContext::ConstantEvaluated, + /*LambdaContextDecl=*/nullptr, + Sema::ExpressionEvaluationContextRecord::EK_Other, IsConstexpr); if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc, IsConstexpr ? Sema::ConditionKind::ConstexprIf @@ -1557,6 +1561,9 @@ if (NotLocation.isInvalid() && IsConsteval) { Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext; ShouldEnter = true; + } else if (NotLocation.isValid() && IsConsteval) { + Context = Sema::ExpressionEvaluationContext::RuntimeEvaluated; + ShouldEnter = true; } EnterExpressionEvaluationContext PotentiallyDiscarded( @@ -1602,6 +1609,9 @@ if (NotLocation.isValid() && IsConsteval) { Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext; ShouldEnter = true; + } else if (NotLocation.isInvalid() && IsConsteval) { + Context = Sema::ExpressionEvaluationContext::RuntimeEvaluated; + ShouldEnter = true; } EnterExpressionEvaluationContext PotentiallyDiscarded( Index: clang/lib/Parse/ParseExpr.cpp =================================================================== --- clang/lib/Parse/ParseExpr.cpp +++ clang/lib/Parse/ParseExpr.cpp @@ -237,6 +237,7 @@ ExprResult Parser::ParseConstraintExpression() { EnterExpressionEvaluationContext ConstantEvaluated( Actions, Sema::ExpressionEvaluationContext::Unevaluated); + Actions.ExprEvalContexts.back().InConstantEvaluated = true; ExprResult LHS(ParseCastExpression(AnyCastExpr)); ExprResult Res(ParseRHSOfBinaryExpression(LHS, prec::LogicalOr)); if (Res.isUsable() && !Actions.CheckConstraintExpression(Res.get())) { Index: clang/lib/Parse/ParseDeclCXX.cpp =================================================================== --- clang/lib/Parse/ParseDeclCXX.cpp +++ clang/lib/Parse/ParseDeclCXX.cpp @@ -3102,7 +3102,8 @@ } else if (HasStaticInitializer) { // Normal initializer. ExprResult Init = ParseCXXMemberInitializer( - ThisDecl, DeclaratorInfo.isDeclarationOfFunction(), EqualLoc); + ThisDecl, DeclaratorInfo.isDeclarationOfFunction(), + DeclaratorInfo.getDeclSpec().hasConstexprSpecifier(), EqualLoc); if (Init.isInvalid()) { if (ThisDecl) @@ -3205,6 +3206,7 @@ /// Prior to C++0x, the assignment-expression in an initializer-clause must /// be a constant-expression. ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction, + bool IsConstexpr, SourceLocation &EqualLoc) { assert(Tok.isOneOf(tok::equal, tok::l_brace) && "Data member initializer not starting with '=' or '{'"); @@ -3215,6 +3217,8 @@ ? Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed : Sema::ExpressionEvaluationContext::PotentiallyEvaluated, D); + Actions.ExprEvalContexts.back().InConstantEvaluated = + !IsFunction && IsConstexpr; Actions.ExprEvalContexts.back().InImmediateEscalatingFunctionContext = true; if (TryConsumeToken(tok::equal, EqualLoc)) { if (Tok.is(tok::kw_delete)) { Index: clang/lib/Parse/ParseDecl.cpp =================================================================== --- clang/lib/Parse/ParseDecl.cpp +++ clang/lib/Parse/ParseDecl.cpp @@ -2384,6 +2384,15 @@ return ParseDeclarationAfterDeclaratorAndAttributes(D, TemplateInfo); } +/// Determine whether the given declaration is a global variable or +/// static data member. +static bool isNonlocalVariable(const Decl *D) { + if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(D)) + return Var->hasGlobalStorage(); + + return false; +} + Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes( Declarator &D, const ParsedTemplateInfo &TemplateInfo, ForRangeInit *FRI) { // RAII type used to track whether we're inside an initializer. @@ -2416,6 +2425,33 @@ ThisDecl = nullptr; } }; + struct EnterInitializerExpressionEvaluationContext { + Sema &S; + bool Entered; + + EnterInitializerExpressionEvaluationContext(Sema &S, Declarator &D, + Decl *ThisDecl) + : S(S), Entered(false) { + if (ThisDecl && S.getLangOpts().CPlusPlus && !ThisDecl->isInvalidDecl()) { + Entered = true; + Sema::ExpressionEvaluationContext NewEEC = + S.ExprEvalContexts.back().Context; + if ((D.getDeclSpec().getTypeQualifiers() == DeclSpec::TQ_const && + S.ExprEvalContexts.back().Context == + Sema::ExpressionEvaluationContext::RuntimeEvaluated) || + isNonlocalVariable(ThisDecl)) { + NewEEC = Sema::ExpressionEvaluationContext::PotentiallyEvaluated; + } + if (D.getDeclSpec().hasConstexprSpecifier()) + NewEEC = Sema::ExpressionEvaluationContext::ConstantEvaluated; + S.PushExpressionEvaluationContext(NewEEC, ThisDecl); + } + } + ~EnterInitializerExpressionEvaluationContext() { + if (Entered) + S.PopExpressionEvaluationContext(); + } + }; enum class InitKind { Uninitialized, Equal, CXXDirect, CXXBraced }; InitKind TheInitKind; @@ -2514,6 +2550,7 @@ << getLangOpts().CPlusPlus20; } else { InitializerScopeRAII InitScope(*this, D, ThisDecl); + EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl); if (Tok.is(tok::code_completion)) { cutOffParsing(); @@ -2561,6 +2598,7 @@ ExprVector Exprs; InitializerScopeRAII InitScope(*this, D, ThisDecl); + EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl); auto ThisVarDecl = dyn_cast_or_null<VarDecl>(ThisDecl); auto RunSignatureHelp = [&]() { @@ -2611,6 +2649,7 @@ Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); InitializerScopeRAII InitScope(*this, D, ThisDecl); + EnterInitializerExpressionEvaluationContext InitEC(Actions, D, ThisDecl); PreferredType.enterVariableInit(Tok.getLocation(), ThisDecl); ExprResult Init(ParseBraceInitializer()); Index: clang/lib/Parse/ParseCXXInlineMethods.cpp =================================================================== --- clang/lib/Parse/ParseCXXInlineMethods.cpp +++ clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -656,7 +656,7 @@ Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed); ExprResult Init = ParseCXXMemberInitializer(MI.Field, /*IsFunction=*/false, - EqualLoc); + /*IsConstexpr=*/false, EqualLoc); Actions.ActOnFinishCXXInClassMemberInitializer(MI.Field, EqualLoc, Init.get()); Index: clang/lib/AST/ExprConstant.cpp =================================================================== --- clang/lib/AST/ExprConstant.cpp +++ clang/lib/AST/ExprConstant.cpp @@ -12093,21 +12093,6 @@ } case Builtin::BI__builtin_is_constant_evaluated: { - const auto *Callee = Info.CurrentCall->getCallee(); - if (Info.InConstantContext && !Info.CheckingPotentialConstantExpression && - (Info.CallStackDepth == 1 || - (Info.CallStackDepth == 2 && Callee->isInStdNamespace() && - Callee->getIdentifier() && - Callee->getIdentifier()->isStr("is_constant_evaluated")))) { - // FIXME: Find a better way to avoid duplicated diagnostics. - if (Info.EvalStatus.Diag) - Info.report((Info.CallStackDepth == 1) ? E->getExprLoc() - : Info.CurrentCall->CallLoc, - diag::warn_is_constant_evaluated_always_true_constexpr) - << (Info.CallStackDepth == 1 ? "__builtin_is_constant_evaluated" - : "std::is_constant_evaluated"); - } - return Success(Info.InConstantContext, E); } Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -1269,7 +1269,12 @@ /// we would like to provide diagnostics (e.g., passing non-POD arguments /// through varargs) but do not want to mark declarations as "referenced" /// until the default argument is used. - PotentiallyEvaluatedIfUsed + PotentiallyEvaluatedIfUsed, + + /// The immediate occurances of consteval if or std::is_constant_evaluated() + /// are tautologically false. Otherwise this should be handled as the same + /// as PotentiallyEvaluated. + RuntimeEvaluated }; using ImmediateInvocationCandidate = llvm::PointerIntPair<ConstantExpr *, 1>; @@ -1333,6 +1338,7 @@ // A context can be nested in both a discarded statement context and // an immediate function context, so they need to be tracked independently. bool InDiscardedStatement; + bool InConstantEvaluated; bool InImmediateFunctionContext; bool InImmediateEscalatingFunctionContext; @@ -1363,7 +1369,8 @@ : Context(Context), ParentCleanup(ParentCleanup), NumCleanupObjects(NumCleanupObjects), NumTypos(0), ManglingContextDecl(ManglingContextDecl), ExprContext(ExprContext), - InDiscardedStatement(false), InImmediateFunctionContext(false), + InDiscardedStatement(false), InConstantEvaluated(false), + InImmediateFunctionContext(false), InImmediateEscalatingFunctionContext(false) {} bool isUnevaluated() const { @@ -9792,7 +9799,8 @@ assert(!ExprEvalContexts.empty() && "Must be in an expression evaluation context"); for (const auto &Ctx : llvm::reverse(ExprEvalContexts)) { - if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated && + if ((Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated || + Ctx.Context == ExpressionEvaluationContext::RuntimeEvaluated) && Ctx.DelayedDefaultInitializationContext) return Ctx.DelayedDefaultInitializationContext; if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() || @@ -9808,7 +9816,8 @@ "Must be in an expression evaluation context"); std::optional<ExpressionEvaluationContextRecord::InitializationContext> Res; for (auto &Ctx : llvm::reverse(ExprEvalContexts)) { - if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated && + if ((Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated || + Ctx.Context == ExpressionEvaluationContext::RuntimeEvaluated) && !Ctx.DelayedDefaultInitializationContext && Res) break; if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() || Index: clang/include/clang/Parse/Parser.h =================================================================== --- clang/include/clang/Parse/Parser.h +++ clang/include/clang/Parse/Parser.h @@ -3245,6 +3245,7 @@ ParsedAttributes &Attrs, unsigned TagType, Decl *TagDecl); ExprResult ParseCXXMemberInitializer(Decl *D, bool IsFunction, + bool IsConstexpr, SourceLocation &EqualLoc); bool ParseCXXMemberDeclaratorBeforeInitializer(Declarator &DeclaratorInfo, Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -1544,7 +1544,7 @@ "expression evaluates to '%0 %1 %2'">; def warn_consteval_if_always_true : Warning< - "consteval if is always true in an %select{unevaluated|immediate}0 context">, + "consteval if is always %select{true|false}0 in this context">, InGroup<DiagGroup<"redundant-consteval-if">>; def ext_inline_variable : ExtWarn< @@ -8820,6 +8820,9 @@ def warn_side_effects_typeid : Warning< "expression with side effects will be evaluated despite being used as an " "operand to 'typeid'">, InGroup<PotentiallyEvaluatedExpression>; +def warn_is_constant_evaluated_tauto_constexpr : Warning< + "'%select{std::is_constant_evaluated|__builtin_is_constant_evaluated}0' will always evaluate to %select{false|true}1 in this context">, + InGroup<DiagGroup<"constant-evaluated">>; def warn_unused_result : Warning< "ignoring return value of function declared with %0 attribute">, InGroup<UnusedResult>; Index: clang/include/clang/Basic/DiagnosticASTKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticASTKinds.td +++ clang/include/clang/Basic/DiagnosticASTKinds.td @@ -408,10 +408,6 @@ def note_unimplemented_constexpr_lambda_feature_ast : Note< "unimplemented constexpr lambda feature: %0 (coming soon!)">; -def warn_is_constant_evaluated_always_true_constexpr : Warning< - "'%0' will always evaluate to 'true' in a manifestly constant-evaluated expression">, - InGroup<DiagGroup<"constant-evaluated">>; - // inline asm related. let CategoryName = "Inline Assembly Issue" in { def err_asm_invalid_escape : Error< Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -384,6 +384,9 @@ (`#57081: <https://github.com/llvm/llvm-project/issues/57081>`_) - Clang no longer emits inappropriate notes about the loss of ``__unaligned`` qualifier on overload resolution, when the actual reason for the failure is loss of other qualifiers. +- Clang now diagnoses wider cases of tautological use of consteval if or std::is_constant_evaluated. + (`#43760: <https://github.com/llvm/llvm-project/issues/43760>`_) + (`#51567: <https://github.com/llvm/llvm-project/issues/51567>`_) Bug Fixes in This Version -------------------------
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits