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

Reply via email to