[clang] 683e83c - [Clang][C++2b] P2242R3: Non-literal variables [...] in constexpr

Corentin Jabot via cfe-commits cfe-commits at lists.llvm.org
Tue Mar 22 11:51:25 PDT 2022


Author: Corentin Jabot
Date: 2022-03-22T19:51:19+01:00
New Revision: 683e83c56f98df6fe42d506a04dda44309ca758f

URL: https://github.com/llvm/llvm-project/commit/683e83c56f98df6fe42d506a04dda44309ca758f
DIFF: https://github.com/llvm/llvm-project/commit/683e83c56f98df6fe42d506a04dda44309ca758f.diff

LOG: [Clang][C++2b] P2242R3: Non-literal variables [...] in constexpr

Allow goto, labelled statements as well as `static`, `thread_local`, and
non-literal variables in `constexpr` functions.

As specified. for all of the above (except labelled statements) constant
evaluation of the construct still fails.

For `constexpr` bodies, the proposal is implemented with diagnostics as
a language extension in older language modes. For determination of
whether a lambda body satisfies the requirements for a constexpr
function, the proposal is implemented only in C++2b mode to retain the
semantics of older modes for programs conforming to them.

Reviewed By: aaron.ballman, hubert.reinterpretcast, erichkeane

Differential Revision: https://reviews.llvm.org/D111400

Added: 
    clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp
    clang/test/SemaCXX/constant-expression-cxx2b.cpp

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/include/clang/Basic/DiagnosticASTKinds.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/AST/ExprConstant.cpp
    clang/lib/Frontend/InitPreprocessor.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp
    clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
    clang/test/Lexer/cxx-features.cpp
    clang/test/SemaCXX/constant-expression-cxx14.cpp
    clang/www/cxx_status.html

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9864019fda09b..c30512c80e0b7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -147,6 +147,7 @@ C++2b Feature Support
 
 - Implemented `P2128R6: Multidimensional subscript operator <https://wg21.link/P2128R6>`_.
 - Implemented `P0849R8: auto(x): decay-copy in the language <https://wg21.link/P0849R8>`_.
+- Implemented `P2242R3: Non-literal variables (and labels and gotos) in constexpr functions	<https://wg21.link/P2242R3>`_.
 
 CUDA Language Changes in Clang
 ------------------------------

diff  --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 56662bcd0cc25..930264a495495 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -65,6 +65,8 @@ def note_consteval_address_accessible : Note<
   "is not a constant expression">;
 def note_constexpr_uninitialized : Note<
   "%select{|sub}0object of type %1 is not initialized">;
+def note_constexpr_static_local : Note<
+  "control flows through the definition of a %select{static|thread_local}0 variable">;
 def note_constexpr_subobject_declared_here : Note<
   "subobject declared here">;
 def note_constexpr_array_index : Note<"cannot refer to element %0 of "

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fea7461e2dc99..dbde2af216d97 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2693,6 +2693,13 @@ def warn_cxx17_compat_constexpr_body_invalid_stmt : Warning<
   "use of this statement in a constexpr %select{function|constructor}0 "
   "is incompatible with C++ standards before C++20">,
   InGroup<CXXPre20Compat>, DefaultIgnore;
+def ext_constexpr_body_invalid_stmt_cxx2b : ExtWarn<
+  "use of this statement in a constexpr %select{function|constructor}0 "
+  "is a C++2b extension">, InGroup<CXX2b>;
+def warn_cxx20_compat_constexpr_body_invalid_stmt : Warning<
+  "use of this statement in a constexpr %select{function|constructor}0 "
+  "is incompatible with C++ standards before C++2b">,
+  InGroup<CXXPre2bCompat>, DefaultIgnore;
 def ext_constexpr_type_definition : ExtWarn<
   "type definition in a constexpr %select{function|constructor}0 "
   "is a C++14 extension">, InGroup<CXX14>;
@@ -2710,12 +2717,18 @@ def warn_cxx11_compat_constexpr_local_var : Warning<
   "variable declaration in a constexpr %select{function|constructor}0 "
   "is incompatible with C++ standards before C++14">,
   InGroup<CXXPre14Compat>, DefaultIgnore;
-def err_constexpr_local_var_static : Error<
-  "%select{static|thread_local}1 variable not permitted in a constexpr "
-  "%select{function|constructor}0">;
+def ext_constexpr_static_var : ExtWarn<
+  "definition of a %select{static|thread_local}1 variable "
+  "in a constexpr %select{function|constructor}0 "
+  "is a C++2b extension">, InGroup<CXX2b>;
+def warn_cxx20_compat_constexpr_static_var : Warning<
+  "definition of a %select{static|thread_local}1 variable "
+  "in a constexpr %select{function|constructor}0 "
+  "is incompatible with C++ standards before C++2b">,
+  InGroup<CXXPre2bCompat>, DefaultIgnore;
 def err_constexpr_local_var_non_literal_type : Error<
   "variable of non-literal type %1 cannot be defined in a constexpr "
-  "%select{function|constructor}0">;
+  "%select{function|constructor}0 before C++2b">;
 def ext_constexpr_local_var_no_init : ExtWarn<
   "uninitialized variable in a constexpr %select{function|constructor}0 "
   "is a C++20 extension">, InGroup<CXX20>;

diff  --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 99f136a72d6fe..78f7592471ddb 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -5003,6 +5003,18 @@ static EvalStmtResult EvaluateSwitch(StmtResult &Result, EvalInfo &Info,
   llvm_unreachable("Invalid EvalStmtResult!");
 }
 
+static bool CheckLocalVariableDeclaration(EvalInfo &Info, const VarDecl *VD) {
+  // An expression E is a core constant expression unless the evaluation of E
+  // would evaluate one of the following: [C++2b] - a control flow that passes
+  // through a declaration of a variable with static or thread storage duration.
+  if (VD->isLocalVarDecl() && VD->isStaticLocal()) {
+    Info.CCEDiag(VD->getLocation(), diag::note_constexpr_static_local)
+        << (VD->getTSCSpec() == TSCS_unspecified ? 0 : 1) << VD;
+    return false;
+  }
+  return true;
+}
+
 // Evaluate a statement.
 static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
                                    const Stmt *S, const SwitchCase *Case) {
@@ -5113,6 +5125,8 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
       const DeclStmt *DS = cast<DeclStmt>(S);
       for (const auto *D : DS->decls()) {
         if (const auto *VD = dyn_cast<VarDecl>(D)) {
+          if (!CheckLocalVariableDeclaration(Info, VD))
+            return ESR_Failed;
           if (VD->hasLocalStorage() && !VD->getInit())
             if (!EvaluateVarDecl(Info, VD))
               return ESR_Failed;
@@ -5156,6 +5170,9 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
   case Stmt::DeclStmtClass: {
     const DeclStmt *DS = cast<DeclStmt>(S);
     for (const auto *D : DS->decls()) {
+      const VarDecl *VD = dyn_cast_or_null<VarDecl>(D);
+      if (VD && !CheckLocalVariableDeclaration(Info, VD))
+        return ESR_Failed;
       // Each declaration initialization is its own full-expression.
       FullExpressionRAII Scope(Info);
       if (!EvaluateDecl(Info, D) && !Info.noteFailure())

diff  --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp
index 6b7c743d4004f..9d7a429bda184 100644
--- a/clang/lib/Frontend/InitPreprocessor.cpp
+++ b/clang/lib/Frontend/InitPreprocessor.cpp
@@ -561,10 +561,11 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
     Builder.defineMacro("__cpp_unicode_literals", "200710L");
     Builder.defineMacro("__cpp_user_defined_literals", "200809L");
     Builder.defineMacro("__cpp_lambdas", "200907L");
-    Builder.defineMacro("__cpp_constexpr",
-                        LangOpts.CPlusPlus20 ? "201907L" :
-                        LangOpts.CPlusPlus17 ? "201603L" :
-                        LangOpts.CPlusPlus14 ? "201304L" : "200704");
+    Builder.defineMacro("__cpp_constexpr", LangOpts.CPlusPlus2b   ? "202110L"
+                                           : LangOpts.CPlusPlus20 ? "201907L"
+                                           : LangOpts.CPlusPlus17 ? "201603L"
+                                           : LangOpts.CPlusPlus14 ? "201304L"
+                                                                  : "200704");
     Builder.defineMacro("__cpp_constexpr_in_decltype", "201711L");
     Builder.defineMacro("__cpp_range_based_for",
                         LangOpts.CPlusPlus17 ? "201603L" : "200907");

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 2a944913be391..6ae88ce3d86a4 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1892,13 +1892,17 @@ static bool CheckConstexprDeclStmt(Sema &SemaRef, const FunctionDecl *Dcl,
         if (VD->isStaticLocal()) {
           if (Kind == Sema::CheckConstexprKind::Diagnose) {
             SemaRef.Diag(VD->getLocation(),
-                         diag::err_constexpr_local_var_static)
-              << isa<CXXConstructorDecl>(Dcl)
-              << (VD->getTLSKind() == VarDecl::TLS_Dynamic);
+                         SemaRef.getLangOpts().CPlusPlus2b
+                             ? diag::warn_cxx20_compat_constexpr_static_var
+                             : diag::ext_constexpr_static_var)
+                << isa<CXXConstructorDecl>(Dcl)
+                << (VD->getTLSKind() == VarDecl::TLS_Dynamic);
+          } else if (!SemaRef.getLangOpts().CPlusPlus2b) {
+            return false;
           }
-          return false;
         }
-        if (CheckLiteralType(SemaRef, Kind, VD->getLocation(), VD->getType(),
+        if (!SemaRef.LangOpts.CPlusPlus2b &&
+            CheckLiteralType(SemaRef, Kind, VD->getLocation(), VD->getType(),
                              diag::err_constexpr_local_var_non_literal_type,
                              isa<CXXConstructorDecl>(Dcl)))
           return false;
@@ -2021,6 +2025,7 @@ static bool
 CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
                            SmallVectorImpl<SourceLocation> &ReturnStmts,
                            SourceLocation &Cxx1yLoc, SourceLocation &Cxx2aLoc,
+                           SourceLocation &Cxx2bLoc,
                            Sema::CheckConstexprKind Kind) {
   // - its function-body shall be [...] a compound-statement that contains only
   switch (S->getStmtClass()) {
@@ -2053,9 +2058,9 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
   case Stmt::AttributedStmtClass:
     // Attributes on a statement don't affect its formal kind and hence don't
     // affect its validity in a constexpr function.
-    return CheckConstexprFunctionStmt(SemaRef, Dcl,
-                                      cast<AttributedStmt>(S)->getSubStmt(),
-                                      ReturnStmts, Cxx1yLoc, Cxx2aLoc, Kind);
+    return CheckConstexprFunctionStmt(
+        SemaRef, Dcl, cast<AttributedStmt>(S)->getSubStmt(), ReturnStmts,
+        Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind);
 
   case Stmt::CompoundStmtClass: {
     // C++1y allows compound-statements.
@@ -2065,7 +2070,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
     CompoundStmt *CompStmt = cast<CompoundStmt>(S);
     for (auto *BodyIt : CompStmt->body()) {
       if (!CheckConstexprFunctionStmt(SemaRef, Dcl, BodyIt, ReturnStmts,
-                                      Cxx1yLoc, Cxx2aLoc, Kind))
+                                      Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
         return false;
     }
     return true;
@@ -2078,11 +2083,11 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
 
     IfStmt *If = cast<IfStmt>(S);
     if (!CheckConstexprFunctionStmt(SemaRef, Dcl, If->getThen(), ReturnStmts,
-                                    Cxx1yLoc, Cxx2aLoc, Kind))
+                                    Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
       return false;
     if (If->getElse() &&
         !CheckConstexprFunctionStmt(SemaRef, Dcl, If->getElse(), ReturnStmts,
-                                    Cxx1yLoc, Cxx2aLoc, Kind))
+                                    Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
       return false;
     return true;
   }
@@ -2101,7 +2106,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
     for (Stmt *SubStmt : S->children())
       if (SubStmt &&
           !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts,
-                                      Cxx1yLoc, Cxx2aLoc, Kind))
+                                      Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
         return false;
     return true;
 
@@ -2116,7 +2121,18 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
     for (Stmt *SubStmt : S->children())
       if (SubStmt &&
           !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts,
-                                      Cxx1yLoc, Cxx2aLoc, Kind))
+                                      Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
+        return false;
+    return true;
+
+  case Stmt::LabelStmtClass:
+  case Stmt::GotoStmtClass:
+    if (Cxx2bLoc.isInvalid())
+      Cxx2bLoc = S->getBeginLoc();
+    for (Stmt *SubStmt : S->children())
+      if (SubStmt &&
+          !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts,
+                                      Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
         return false;
     return true;
 
@@ -2129,7 +2145,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
     for (Stmt *SubStmt : S->children()) {
       if (SubStmt &&
           !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts,
-                                      Cxx1yLoc, Cxx2aLoc, Kind))
+                                      Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
         return false;
     }
     return true;
@@ -2137,9 +2153,9 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S,
   case Stmt::CXXCatchStmtClass:
     // Do not bother checking the language mode (already covered by the
     // try block check).
-    if (!CheckConstexprFunctionStmt(SemaRef, Dcl,
-                                    cast<CXXCatchStmt>(S)->getHandlerBlock(),
-                                    ReturnStmts, Cxx1yLoc, Cxx2aLoc, Kind))
+    if (!CheckConstexprFunctionStmt(
+            SemaRef, Dcl, cast<CXXCatchStmt>(S)->getHandlerBlock(), ReturnStmts,
+            Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
       return false;
     return true;
 
@@ -2204,20 +2220,27 @@ static bool CheckConstexprFunctionBody(Sema &SemaRef, const FunctionDecl *Dcl,
   //
   // Note that walking the children here is enough to properly check for
   // CompoundStmt and CXXTryStmt body.
-  SourceLocation Cxx1yLoc, Cxx2aLoc;
+  SourceLocation Cxx1yLoc, Cxx2aLoc, Cxx2bLoc;
   for (Stmt *SubStmt : Body->children()) {
     if (SubStmt &&
         !CheckConstexprFunctionStmt(SemaRef, Dcl, SubStmt, ReturnStmts,
-                                    Cxx1yLoc, Cxx2aLoc, Kind))
+                                    Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind))
       return false;
   }
 
   if (Kind == Sema::CheckConstexprKind::CheckValid) {
     // If this is only valid as an extension, report that we don't satisfy the
     // constraints of the current language.
-    if ((Cxx2aLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus20) ||
+    if ((Cxx2bLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus2b) ||
+        (Cxx2aLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus20) ||
         (Cxx1yLoc.isValid() && !SemaRef.getLangOpts().CPlusPlus17))
       return false;
+  } else if (Cxx2bLoc.isValid()) {
+    SemaRef.Diag(Cxx2bLoc,
+                 SemaRef.getLangOpts().CPlusPlus2b
+                     ? diag::warn_cxx20_compat_constexpr_body_invalid_stmt
+                     : diag::ext_constexpr_body_invalid_stmt_cxx2b)
+        << isa<CXXConstructorDecl>(Dcl);
   } else if (Cxx2aLoc.isValid()) {
     SemaRef.Diag(Cxx2aLoc,
          SemaRef.getLangOpts().CPlusPlus20

diff  --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp
index 3d45949430f99..dcb9f60bdd2d2 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/dtor.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -std=c++2a -verify %s
+// RUN: %clang_cc1 -std=c++2a -verify=expected,cxx2a %s
+// RUN: %clang_cc1 -std=c++2b -verify=expected %s
 
 // p3: if the function is a constructor or destructor, its class shall not have
 // any virtual base classes;
@@ -9,40 +10,43 @@ namespace vbase {
   };
 }
 
-// p3: its function-body shall not enclose
-//  -- a goto statement
-//  -- an identifier label
-//  -- a variable of non-literal type or of static or thread storage duration
 namespace contents {
   struct A {
     constexpr ~A() {
-      goto x; // expected-error {{statement not allowed in constexpr function}}
+      return;
+      goto x; // cxx2a-warning {{use of this statement in a constexpr function is a C++2b extension}}
       x: ;
     }
   };
   struct B {
     constexpr ~B() {
-      x: ; // expected-error {{statement not allowed in constexpr function}}
+    x:; // cxx2a-warning {{use of this statement in a constexpr function is a C++2b extension}}
     }
   };
-  struct Nonlit { Nonlit(); }; // expected-note {{not literal}}
+  struct Nonlit { // cxx2a-note {{'Nonlit' is not literal because}}
+    Nonlit();
+  };
   struct C {
     constexpr ~C() {
-      Nonlit nl; // expected-error {{non-literal}}
+      return;
+      Nonlit nl; // cxx2a-error {{variable of non-literal type 'contents::Nonlit' cannot be defined in a constexpr function before C++2b}}
     }
   };
   struct D {
     constexpr ~D() {
-      static int a; // expected-error {{static variable}}
+      return;
+      static int a; // cxx2a-warning {{definition of a static variable in a constexpr function is a C++2b extension}}
     }
   };
   struct E {
     constexpr ~E() {
-      thread_local int e; // expected-error {{thread_local variable}}
+      return;
+      thread_local int e; // cxx2a-warning {{definition of a thread_local variable in a constexpr function is a C++2b extension}}
     }
   };
   struct F {
     constexpr ~F() {
+      return;
       extern int f;
     }
   };

diff  --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp
new file mode 100644
index 0000000000000..671895b278bdf
--- /dev/null
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3-2b.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -verify -std=c++2b -Wpre-c++2b-compat %s
+
+constexpr int h(int n) {
+  if (!n)
+    return 0;
+  static const int m = n; // expected-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int i(int n) {
+  if (!n)
+    return 0;
+  thread_local const int m = n; // expected-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int g() { // expected-error {{constexpr function never produces a constant expression}}
+  goto test;        // expected-note {{subexpression not valid in a constant expression}} \
+           // expected-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}}
+test:
+  return 0;
+}
+
+constexpr void h() {
+label:; // expected-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}}
+}
+
+struct NonLiteral {
+  NonLiteral() {}
+};
+
+constexpr void non_literal() { // expected-error {{constexpr function never produces a constant expression}}
+  NonLiteral n;                // expected-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}}
+}
+
+constexpr void non_literal2(bool b) {
+  if (!b)
+    NonLiteral n;
+}
+
+constexpr int c_thread_local(int n) {
+  if (!n)
+    return 0;
+  static _Thread_local int a; // expected-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  _Thread_local int b;        // // expected-error {{'_Thread_local' variables must have global storage}}
+  return 0;
+}
+
+constexpr int gnu_thread_local(int n) {
+  if (!n)
+    return 0;
+  static __thread int a; // expected-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  __thread int b;        // expected-error {{'__thread' variables must have global storage}}
+  return 0;
+}

diff  --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
index 9b0e7cc60f101..3dbe3e9a7212b 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
@@ -1,6 +1,7 @@
-// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++11 -Werror=c++14-extensions -Werror=c++20-extensions %s
-// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++14 -DCXX14 -Werror=c++20-extensions %s
-// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++20 -DCXX14 -DCXX20 %s
+// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++11 -Werror=c++14-extensions -Werror=c++20-extensions -Werror=c++2b-extensions %s
+// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++14 -DCXX14 -Werror=c++20-extensions -Werror=c++2b-extensions %s
+// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++20 -DCXX14 -DCXX20 -Werror=c++2b-extensions %s
+// RUN: %clang_cc1 -verify -fcxx-exceptions -triple=x86_64-linux-gnu -std=c++2b -DCXX14 -DCXX20 -DCXX2b %s
 
 namespace N {
   typedef char C;
@@ -10,7 +11,7 @@ namespace M {
   typedef double D;
 }
 
-struct NonLiteral { // expected-note 3{{no constexpr constructors}}
+struct NonLiteral { // expected-note 2{{no constexpr constructors}}
   NonLiteral() {}
   NonLiteral(int) {}
 };
@@ -150,16 +151,26 @@ constexpr int DisallowedStmtsCXX14_1(bool b) {
 }
 constexpr int DisallowedStmtsCXX14_2() {
   //  - a goto statement
-  goto x; // expected-error {{statement not allowed in constexpr function}}
-x:
+  try {
+  } catch (...) {
+    goto x;
+  x:;
+  }
+#ifndef CXX2b
+  // expected-error at -4 {{use of this statement in a constexpr function is a C++2b extension}}
+#endif
   return 0;
 }
 constexpr int DisallowedStmtsCXX14_2_1() {
   try {
-    return 0;
   } catch (...) {
-  merp: goto merp; // expected-error {{statement not allowed in constexpr function}}
+  merp:
+    goto merp;
+#ifndef CXX2b
+    // expected-error at -3 {{use of this statement in a constexpr function is a C++2b extension}}
+#endif
   }
+  return 0;
 }
 constexpr int DisallowedStmtsCXX14_3() {
   //  - a try-block,
@@ -171,18 +182,35 @@ constexpr int DisallowedStmtsCXX14_3() {
 }
 constexpr int DisallowedStmtsCXX14_4() {
   //  - a definition of a variable of non-literal type
-  NonLiteral nl; // expected-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function}}
   return 0;
+  NonLiteral nl;
+#ifndef CXX2b
+  // expected-error at -2 {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++2b}}
+  // expected-note at 14  {{'NonLiteral' is not literal}}
+#endif
 }
+
 constexpr int DisallowedStmtsCXX14_5() {
+  return 0;
   //  - a definition of a variable of static storage duration
-  static constexpr int n = 123; // expected-error {{static variable not permitted in a constexpr function}}
-  return n;
+  static constexpr int n = 123;
+#ifndef CXX2b
+  // expected-error at -2 {{definition of a static variable in a constexpr function is a C++2b extension}}
+#endif
+#if !defined(CXX14)
+  // expected-error at -5 {{variable declaration in a constexpr function is a C++14 extension}}
+#endif
 }
 constexpr int DisallowedStmtsCXX14_6() {
   //  - a definition of a variable of thread storage duration
-  thread_local constexpr int n = 123; // expected-error {{thread_local variable not permitted in a constexpr function}}
-  return n;
+  return 0;
+  thread_local constexpr int n = 123;
+#ifndef CXX2b
+  // expected-error at -2 {{definition of a thread_local variable in a constexpr function is a C++2b extension}}
+#endif
+#if !defined(CXX14)
+  // expected-error at -5 {{variable declaration in a constexpr function is a C++14 extension}}
+#endif
 }
 constexpr int DisallowedStmtsCXX14_7() {
   //  - a definition of a variable for which no initialization is performed
@@ -317,8 +345,14 @@ namespace std_example {
     return x;
   }
   constexpr int first(int n) {
-    static int value = n; // expected-error {{static variable not permitted}}
-    return value;
+    return 0;
+    static int value = n;
+#ifndef CXX2b
+    // expected-error at -2 {{definition of a static variable in a constexpr function is a C++2b extension}}
+#endif
+#ifndef CXX14
+    // expected-error at -5 {{variable declaration in a constexpr function is a C++14 extension}}
+#endif
   }
   constexpr int uninit() {
     int a;

diff  --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index 2847273d1d3af..6b6729df15bd1 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/test/Lexer/cxx-features.cpp
@@ -277,7 +277,7 @@
 #error "wrong value for __cpp_lambdas"
 #endif
 
-#if check(constexpr, 0, 200704, 201304, 201603, 201907, 201907)
+#if check(constexpr, 0, 200704, 201304, 201603, 201907, 202110)
 #error "wrong value for __cpp_constexpr"
 #endif
 

diff  --git a/clang/test/SemaCXX/constant-expression-cxx14.cpp b/clang/test/SemaCXX/constant-expression-cxx14.cpp
index 84ffad3707891..9f8d8f7954b12 100644
--- a/clang/test/SemaCXX/constant-expression-cxx14.cpp
+++ b/clang/test/SemaCXX/constant-expression-cxx14.cpp
@@ -44,12 +44,14 @@ constexpr int g(int k) {
   return 3 * k3 + 5 * k2 + n * k - 20;
 }
 static_assert(g(2) == 42, "");
-constexpr int h(int n) {
-  static const int m = n; // expected-error {{static variable not permitted in a constexpr function}}
+constexpr int h(int n) {  // expected-error {{constexpr function never produces a constant expression}}
+  static const int m = n; // expected-note {{control flows through the definition of a static variable}} \
+                          // cxx14_20-warning {{definition of a static variable in a constexpr function is a C++2b extension}}
   return m;
 }
-constexpr int i(int n) {
-  thread_local const int m = n; // expected-error {{thread_local variable not permitted in a constexpr function}}
+constexpr int i(int n) {        // expected-error {{constexpr function never produces a constant expression}}
+  thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \
+                                // cxx14_20-warning {{definition of a thread_local variable in a constexpr function is a C++2b extension}}
   return m;
 }
 

diff  --git a/clang/test/SemaCXX/constant-expression-cxx2b.cpp b/clang/test/SemaCXX/constant-expression-cxx2b.cpp
new file mode 100644
index 0000000000000..0657a35947ade
--- /dev/null
+++ b/clang/test/SemaCXX/constant-expression-cxx2b.cpp
@@ -0,0 +1,240 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx2a %s -fcxx-exceptions -triple=x86_64-linux-gnu -Wno-c++2b-extensions
+// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify=expected,cxx2b %s -fcxx-exceptions -triple=x86_64-linux-gnu -Wpre-c++2b-compat
+
+struct NonLiteral { // cxx2a-note {{'NonLiteral' is not literal}}
+  NonLiteral() {}
+};
+
+#if __cplusplus > 202002L
+
+constexpr int f(int n) {  // expected-error {{constexpr function never produces a constant expression}}
+  static const int m = n; // expected-note {{control flows through the definition of a static variable}} \
+                          // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+constexpr int g(int n) {        // expected-error {{constexpr function never produces a constant expression}}
+  thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \
+                                // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int c_thread_local(int n) { // expected-error {{constexpr function never produces a constant expression}}
+  static _Thread_local int m = 0;     // expected-note {{control flows through the definition of a thread_local variable}} \
+                                      // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int gnu_thread_local(int n) { // expected-error {{constexpr function never produces a constant expression}}
+  static __thread int m = 0;            // expected-note {{control flows through the definition of a thread_local variable}} \
+                                        // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int h(int n) {  // expected-error {{constexpr function never produces a constant expression}}
+  static const int m = n; // expected-note {{control flows through the definition of a static variable}} \
+                          // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return &m - &m;
+}
+constexpr int i(int n) {        // expected-error {{constexpr function never produces a constant expression}}
+  thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \
+                                 // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return &m - &m;
+}
+
+constexpr int j(int n) {
+  if (!n)
+    return 0;
+  static const int m = n; // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+constexpr int j0 = j(0);
+
+constexpr int k(int n) {
+  if (!n)
+    return 0;
+  thread_local const int m = n; // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}}
+
+  return m;
+}
+constexpr int k0 = k(0);
+
+constexpr int j_evaluated(int n) {
+  if (!n)
+    return 0;
+  static const int m = n; // expected-note {{control flows through the definition of a static variable}} \
+                          // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int je = j_evaluated(1); // expected-error {{constexpr variable 'je' must be initialized by a constant expression}}  \
+                                   // expected-note {{in call}}
+
+constexpr int k_evaluated(int n) {
+  if (!n)
+    return 0;
+  thread_local const int m = n; // expected-note {{control flows through the definition of a thread_local variable}} \
+                                // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}}
+
+  return m;
+}
+
+constexpr int ke = k_evaluated(1); // expected-error {{constexpr variable 'ke' must be initialized by a constant expression}} \
+                                   // expected-note {{in call}}
+
+constexpr int static_constexpr() { // expected-error {{constexpr function never produces a constant expression}}
+  static constexpr int m = 42;     // expected-note {{control flows through the definition of a static variable}} \
+                                   // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int thread_local_constexpr() { // expected-error {{constexpr function never produces a constant expression}}
+  thread_local constexpr int m = 42;     // expected-note {{control flows through the definition of a thread_local variable}} \
+                                         // cxx2b-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}}
+  return m;
+}
+
+constexpr int non_literal(bool b) {
+  if (!b)
+    return 0;
+  NonLiteral n;
+}
+
+constexpr int non_literal_1 = non_literal(false);
+
+namespace eval_goto {
+
+constexpr int f(int x) {
+  if (x) {
+    return 0;
+  } else {
+    goto test; // expected-note {{subexpression not valid in a constant expression}} \
+               // cxx2b-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}}
+  }
+test:
+  return 0;
+}
+
+int a = f(0);
+constexpr int b = f(0); // expected-error {{must be initialized by a constant expression}} \
+                        // expected-note {{in call to 'f(0)'}}
+constexpr int c = f(1);
+
+constexpr int label() {
+
+test: // cxx2b-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}}
+  return 0;
+}
+
+constexpr int d = label();
+
+} // namespace eval_goto
+
+#endif
+
+// Test that explicitly constexpr lambdas behave correctly,
+// This is to be contrasted with the test for implicitly constexpr lambdas below.
+int test_in_lambdas() {
+  auto a = []() constexpr {  // expected-error{{constexpr function never produces a constant expression}}
+    static const int m = 32; // expected-note {{control flows through the definition of a static variable}} \
+                             // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+    return m;
+  };
+
+  auto b = [](int n) constexpr {
+    if (!n)
+      return 0;
+    static const int m = n; // cxx2b-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}}
+    return m;
+  }
+  (1);
+
+  auto c = [](int n) constexpr {
+    if (!n)
+      return 0;
+    else
+      goto test; // expected-note {{subexpression not valid in a constant expression}} \
+                 // cxx2b-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}}
+  test:
+    return 1;
+  };
+  c(0);
+  constexpr auto c_error = c(1); // expected-error {{constexpr variable 'c_error' must be initialized by a constant expression}} \
+                                 // expected-note {{in call to}}
+
+  auto non_literal = [](bool b) constexpr {
+    if (!b)
+      NonLiteral n; // cxx2b-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}} \
+                    // cxx2a-error {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++2b}}
+    return 0;
+  };
+
+#if __cplusplus > 202002L
+  constexpr auto non_literal_ko = non_literal(false); // cxx2b-error {{constexpr variable 'non_literal_ko' must be initialized by a constant expression}} \
+                                                      // cxx2b-note {{in call}}
+
+  constexpr auto non_literal_ok = non_literal(true);
+#endif
+}
+
+// Test whether lambdas are correctly treated as implicitly constexpr under the
+// relaxed C++23 rules (and similarly as not implicitly constexpr under the
+// C++20 rules).
+int test_lambdas_implicitly_constexpr() {
+
+  auto b = [](int n) { // cxx2a-note 2{{declared here}}
+    if (!n)
+      return 0;
+    static const int m = n; // cxx2b-note {{control flows through the definition of a static variable}}
+    return m;
+  };
+
+  auto b1 = b(1);
+  constexpr auto b2 = b(0); // cxx2a-error {{must be initialized by a constant expression}} \
+                            // cxx2a-note {{non-constexpr function}}
+
+  constexpr auto b3 = b(1); // expected-error{{constexpr variable 'b3' must be initialized by a constant expression}} \
+                            // cxx2a-note {{non-constexpr function}} \
+                            // cxx2b-note {{in call}}
+
+  auto c = [](int n) { // cxx2a-note 2{{declared here}}
+    if (!n)
+      return 0;
+    else
+      goto test; // cxx2b-note {{subexpression not valid in a constant expression}}
+  test:
+    return 1;
+  };
+  c(0);
+  constexpr auto c_ok = c(0); // cxx2a-error {{must be initialized by a constant expression}} \
+                              // cxx2a-note {{non-constexpr function}}
+
+  constexpr auto c_error = c(1); // expected-error {{constexpr variable 'c_error' must be initialized by a constant expression}} \
+                                 // cxx2a-note {{non-constexpr function}} \
+                                 // cxx2b-note {{in call to}}
+
+  auto non_literal = [](bool b) { // cxx2a-note 2{{declared here}}
+    if (b)
+      NonLiteral n; // cxx2b-note {{non-literal type 'NonLiteral' cannot be used in a constant expression}}
+    return 0;
+  };
+
+  constexpr auto non_literal_ko = non_literal(true); // expected-error {{constexpr variable 'non_literal_ko' must be initialized by a constant expression}} \
+                                                     // cxx2a-note {{non-constexpr function}} \
+                                                     // cxx2b-note {{in call}}
+
+  constexpr auto non_literal_ok = non_literal(false); // cxx2a-error {{must be initialized by a constant expression}} \
+                                                      // cxx2a-note {{non-constexpr function}}
+}
+
+template <typename T>
+constexpr auto dependent_var_def_lambda(void) {
+  return [](bool b) { // cxx2a-note {{declared here}}
+    if (!b)
+      T t;
+    return 0;
+  };
+}
+
+constexpr auto non_literal_valid_in_cxx2b = dependent_var_def_lambda<NonLiteral>()(true); // \
+    // cxx2a-error {{constexpr variable 'non_literal_valid_in_cxx2b' must be initialized by a constant expression}} \
+    // cxx2a-note  {{non-constexpr function}}

diff  --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index afeaf8d7dffaf..a7596230da754 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1361,7 +1361,7 @@ <h2 id="cxx23">C++2b implementation status</h2>
     <tr>
       <td>Non-literal variables (and labels and gotos) in constexpr functions</td>
       <td><a href="https://wg21.link/P2242R3">P2242R3</a></td>
-      <td class="none" align="center">No</td>
+      <td class="unreleased" align="center">Clang 15</td>
     </tr>
     <tr>
       <td>Character encoding of diagnostic text</td>


        


More information about the cfe-commits mailing list