diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -127,6 +127,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/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2689,6 +2689,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; @@ -2706,12 +2713,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/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.isValid()) + 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,4 @@ -// RUN: %clang_cc1 -std=c++2a -verify %s +// RUN: %clang_cc1 -std=c++2b -verify %s // p3: if the function is a constructor or destructor, its class shall not have // any virtual base classes; @@ -9,40 +9,42 @@ }; } -// 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; x: ; } }; struct B { constexpr ~B() { - x: ; // expected-error {{statement not allowed in constexpr function}} + x: ; } }; - struct Nonlit { Nonlit(); }; // expected-note {{not literal}} + struct Nonlit { Nonlit(); }; struct C { constexpr ~C() { - Nonlit nl; // expected-error {{non-literal}} + return; + Nonlit nl; } }; struct D { constexpr ~D() { - static int a; // expected-error {{static variable}} + return; + static int a; } }; struct E { constexpr ~E() { - thread_local int e; // expected-error {{thread_local variable}} + return; + thread_local int e; + } }; 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,42 @@ +// 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 f(int x) { + if(x) { + return 0; + } + else { + 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 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; +} + + +int x = f(0); +constexpr int y = f(0); //expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'f(0)'}} + +constexpr void h() { +label:; // expected-warning {{use of this statement in a constexpr function is incompatible with C++ standards before C++2b}} +} 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,25 @@ } 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@-2 {{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 +181,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() { // - 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 + return 0; } 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 +344,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 {{C++14}} +#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,14 +44,6 @@ 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}} - return m; -} -constexpr int i(int n) { - thread_local const int m = n; // expected-error {{thread_local variable not permitted in a constexpr function}} - return m; -} // if-statements can be used in constexpr functions. constexpr int j(int k) { @@ -65,7 +57,9 @@ return 2; } } -} // expected-note 2{{control reached end of constexpr function}} +} // expected-warning {{non-void}} \ + //expected-note 2{{control reached end of constexpr function}} + static_assert(j(0) == -3, ""); static_assert(j(1) == 5, ""); static_assert(j(2), ""); // expected-error {{constant expression}} expected-note {{in call to 'j(2)'}} 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,25 @@ +// RUN: %clang_cc1 -std=c++2b -fsyntax-only -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu -Wpre-c++2b-compat + +constexpr int f(int n) { // expected-error {{constexpr function never produces a constant expression}} + static const int m = n; // expected-note {{declared here}} \ + // expected-warning {{definition of a static variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; // expected-note {{initializer of 'm' is not a constant expression}} +} +constexpr int g(int n) { // expected-error {{constexpr function never produces a constant expression}} + thread_local const int m = n; // expected-note {{declared here}} \ + // expected-warning {{definition of a thread_local variable in a constexpr function is incompatible with C++ standards before C++2b}} + return m; // expected-note {{initializer of 'm' is not a constant expression}} +} + +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; +} diff --git a/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp b/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp --- a/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp +++ b/clang/test/SemaCXX/cxx1z-constexpr-lambdas.cpp @@ -115,11 +115,6 @@ constexpr const char *Str = "abc"; static_assert(fp4(Str) == Str); -auto NCL = [](int i) { static int j; return j; }; //expected-note{{declared here}} -constexpr int (*fp5)(int) = NCL; -constexpr int I = //expected-error{{must be initialized by a constant expression}} - fp5(5); //expected-note{{non-constexpr function}} - namespace test_dont_always_instantiate_constexpr_templates { auto explicit_return_type = [](auto x) -> int { return x.get(); }; 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