diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -147,6 +147,7 @@ - Implemented `P2128R6: Multidimensional subscript operator `_. - Implemented `P0849R8: auto(x): decay-copy in the language `_. +- Implemented `P2242R3: Non-literal variables (and labels and gotos) in constexpr functions `_. CUDA Language Changes in Clang ------------------------------ diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -65,6 +65,8 @@ "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 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2693,6 +2693,13 @@ "use of this statement in a constexpr %select{function|constructor}0 " "is incompatible with C++ standards before C++20">, InGroup, 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; +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, DefaultIgnore; def ext_constexpr_type_definition : ExtWarn< "type definition in a constexpr %select{function|constructor}0 " "is a C++14 extension">, InGroup; @@ -2710,12 +2717,18 @@ "variable declaration in a constexpr %select{function|constructor}0 " "is incompatible with C++ standards before C++14">, InGroup, 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; +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, 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; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -5003,6 +5003,18 @@ 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 @@ const DeclStmt *DS = cast(S); for (const auto *D : DS->decls()) { if (const auto *VD = dyn_cast(D)) { + if (!CheckLocalVariableDeclaration(Info, VD)) + return ESR_Failed; if (VD->hasLocalStorage() && !VD->getInit()) if (!EvaluateVarDecl(Info, VD)) return ESR_Failed; @@ -5156,6 +5170,9 @@ case Stmt::DeclStmtClass: { const DeclStmt *DS = cast(S); for (const auto *D : DS->decls()) { + const VarDecl *VD = dyn_cast_or_null(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 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -561,10 +561,11 @@ 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 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1892,13 +1892,17 @@ if (VD->isStaticLocal()) { if (Kind == Sema::CheckConstexprKind::Diagnose) { SemaRef.Diag(VD->getLocation(), - diag::err_constexpr_local_var_static) - << isa(Dcl) - << (VD->getTLSKind() == VarDecl::TLS_Dynamic); + SemaRef.getLangOpts().CPlusPlus2b + ? diag::warn_cxx20_compat_constexpr_static_var + : diag::ext_constexpr_static_var) + << isa(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(Dcl))) return false; @@ -2021,6 +2025,7 @@ CheckConstexprFunctionStmt(Sema &SemaRef, const FunctionDecl *Dcl, Stmt *S, SmallVectorImpl &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 @@ 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(S)->getSubStmt(), - ReturnStmts, Cxx1yLoc, Cxx2aLoc, Kind); + return CheckConstexprFunctionStmt( + SemaRef, Dcl, cast(S)->getSubStmt(), ReturnStmts, + Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind); case Stmt::CompoundStmtClass: { // C++1y allows compound-statements. @@ -2065,7 +2070,7 @@ CompoundStmt *CompStmt = cast(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 @@ IfStmt *If = cast(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 @@ 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 @@ 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 @@ 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 @@ case Stmt::CXXCatchStmtClass: // Do not bother checking the language mode (already covered by the // try block check). - if (!CheckConstexprFunctionStmt(SemaRef, Dcl, - cast(S)->getHandlerBlock(), - ReturnStmts, Cxx1yLoc, Cxx2aLoc, Kind)) + if (!CheckConstexprFunctionStmt( + SemaRef, Dcl, cast(S)->getHandlerBlock(), ReturnStmts, + Cxx1yLoc, Cxx2aLoc, Cxx2bLoc, Kind)) return false; return true; @@ -2204,20 +2220,27 @@ // // 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(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 --- 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 @@ }; } -// 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 --- /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 --- 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 @@ 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_2() { // - a goto statement - goto x; // expected-error {{statement not allowed in constexpr function}} -x: + try { + } catch (...) { + goto x; + x:; + } +#ifndef CXX2b + // expected-error@-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@-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_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@-2 {{variable of non-literal type 'NonLiteral' cannot be defined in a constexpr function before C++2b}} + // expected-note@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@-2 {{definition of a static variable in a constexpr function is a C++2b extension}} +#endif +#if !defined(CXX14) + // expected-error@-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@-2 {{definition of a thread_local variable in a constexpr function is a C++2b extension}} +#endif +#if !defined(CXX14) + // expected-error@-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 @@ 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@-2 {{definition of a static variable in a constexpr function is a C++2b extension}} +#endif +#ifndef CXX14 + // expected-error@-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 --- 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 --- a/clang/test/SemaCXX/constant-expression-cxx14.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx14.cpp @@ -44,12 +44,14 @@ 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 --- /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 +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()(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 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -1361,7 +1361,7 @@ Non-literal variables (and labels and gotos) in constexpr functions P2242R3 - No + Clang 15 Character encoding of diagnostic text