diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -134,6 +134,8 @@ C++2b Feature Support ^^^^^^^^^^^^^^^^^^^^^ - Implemented `P1938R3: if consteval `_. +- 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 @@ -2678,6 +2678,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; @@ -2695,12 +2702,15 @@ "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 err_constexpr_local_var_non_literal_type : Error< - "variable of non-literal type %1 cannot be defined in a constexpr " - "%select{function|constructor}0">; +def ext_constexpr_static_literal_var : ExtWarn< + "definition of a %select{static variable|thread_local variable|" + "variable of non-literal type}1 in a constexpr %select{function|constructor}0 " + "is a C++2b extension">, InGroup; +def warn_cxx20_compat_constexpr_static_literal_var : Warning< + "definition of a %select{static variable|thread_local variable|" + "variable of non-literal type}1 in a constexpr %select{function|constructor}0 " + "is incompatible with C++ standards before C++2b">, + InGroup, DefaultIgnore; 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 @@ -525,6 +525,7 @@ Builder.defineMacro("__cpp_user_defined_literals", "200809L"); Builder.defineMacro("__cpp_lambdas", "200907L"); Builder.defineMacro("__cpp_constexpr", + LangOpts.CPlusPlus2b ? "202110L" : LangOpts.CPlusPlus20 ? "201907L" : LangOpts.CPlusPlus17 ? "201603L" : LangOpts.CPlusPlus14 ? "201304L" : "200704"); 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 @@ -1891,19 +1891,27 @@ if (VD->isThisDeclarationADefinition()) { 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.Diag( + VD->getLocation(), + SemaRef.getLangOpts().CPlusPlus2b + ? diag::warn_cxx20_compat_constexpr_static_literal_var + : diag::ext_constexpr_static_literal_var) + << isa(Dcl) + << (VD->getTLSKind() == VarDecl::TLS_Dynamic); } - return false; } - if (CheckLiteralType(SemaRef, Kind, VD->getLocation(), VD->getType(), - diag::err_constexpr_local_var_non_literal_type, - isa(Dcl))) - return false; - if (!VD->getType()->isDependentType() && - !VD->hasInit() && !VD->isCXXForRangeDecl()) { + QualType T = VD->getType(); + if (!T->isDependentType() && !T->isLiteralType(SemaRef.Context) && + Kind == Sema::CheckConstexprKind::Diagnose) { + SemaRef.Diag( + VD->getLocation(), + SemaRef.getLangOpts().CPlusPlus2b + ? diag::warn_cxx20_compat_constexpr_static_literal_var + : diag::ext_constexpr_static_literal_var) + << isa(Dcl) << 2; + } + if (!T->isDependentType() && !VD->hasInit() && + !VD->isCXXForRangeDecl()) { if (Kind == Sema::CheckConstexprKind::Diagnose) { SemaRef.Diag( VD->getLocation(), @@ -2021,6 +2029,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 +2062,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 +2074,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 +2087,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 +2110,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 +2125,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 +2149,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 +2157,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 +2224,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/basic/basic.types/p10.cpp b/clang/test/CXX/basic/basic.types/p10.cpp --- a/clang/test/CXX/basic/basic.types/p10.cpp +++ b/clang/test/CXX/basic/basic.types/p10.cpp @@ -136,9 +136,6 @@ }; constexpr int f(ArrBad) { return 0; } // expected-error {{1st parameter type 'ArrBad' is not a literal type}} -constexpr int arb(int n) { - int a[n]; // expected-error {{variable of non-literal type 'int [n]' cannot be defined in a constexpr function}} -} // expected-warning@+1 {{variable length array folded to constant array as an extension}} constexpr long Overflow[(1 << 30) << 2]{}; // expected-warning {{requires 34 bits to represent}} 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,23 @@ +// RUN: %clang_cc1 -verify -std=c++2b %s + + +constexpr int f(int x) { + if(x) { + return 0; + } + else { + goto test; // expected-note {{subexpression not valid in a constant expression}} + } + 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}} +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)'}} 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,8 @@ -// 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 +12,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 +152,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 +182,37 @@ } 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 {{definition of a variable of non-literal type 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_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 +347,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 @@ -273,7 +273,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/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 @@ -1356,7 +1356,7 @@ Non-literal variables (and labels and gotos) in constexpr functions P2242R3 - No + Clang 14 Character encoding of diagnostic text