[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