On Thu, Nov 17, 2022 at 07:42:40PM +0100, Jakub Jelinek via Gcc-patches wrote: > I thought for older C++ this is to catch > void > foo () > { > constexpr int a = ({ static constexpr int b = 2; b; }); > } > and for C++23 the only 3 spots that diagnose those. > But perhaps for C++20 or older we can check if the var has a context > of a constexpr function (then assume cp_finish_decl errored or pedwarned > already) and only error or pedwarn otherwise.
So, here is an updated patch, which in constexpr.cc will accept DECL_EXPR of decl_*constant_var_p static/thread_local non-extern vars for C++23 or if they are not declared in constexpr/consteval function. So, the statement expression case will remain hard error for C++ <= 20 rather than pedwarn, because due to the ctx->quiet vs. !ctx->quiet case I don't see what else we could do, either something is a constant expression, or it is not, but whether it is or is not shouldn't depend on -Wpedantic/-Wno-pedantic/-Werror=pedantic. 2022-11-17 Jakub Jelinek <ja...@redhat.com> gcc/c-family/ * c-cppbuiltin.cc (c_cpp_builtins): Bump __cpp_constexpr value from 202207L to 202211L. gcc/cp/ * constexpr.cc (cxx_eval_constant_expression): Implement C++23 P2647R1 - Permitting static constexpr variables in constexpr functions. Allow decl_constant_var_p static or thread_local vars for C++23 and later or if they are declared inside of constexpr or consteval function. (potential_constant_expression_1): Similarly, except use decl_maybe_constant_var_p instead of decl_constant_var_p if processing_template_decl. * decl.cc (diagnose_static_in_constexpr): New function. (start_decl): Remove diagnostics of static or thread_local vars in constexpr or consteval functions. (cp_finish_decl): Call diagnose_static_in_constexpr. gcc/testsuite/ * g++.dg/cpp23/constexpr-nonlit17.C: New test. * g++.dg/cpp23/constexpr-nonlit18.C: New test. * g++.dg/cpp23/constexpr-nonlit19.C: New test. * g++.dg/cpp23/constexpr-nonlit20.C: New test. * g++.dg/cpp23/feat-cxx2b.C: Adjust expected __cpp_constexpr value. * g++.dg/ext/stmtexpr19.C: Don't expect an error for C++20 or later. --- gcc/c-family/c-cppbuiltin.cc.jj 2022-11-17 09:00:42.106249011 +0100 +++ gcc/c-family/c-cppbuiltin.cc 2022-11-17 09:01:49.286320527 +0100 @@ -1074,7 +1074,7 @@ c_cpp_builtins (cpp_reader *pfile) /* Set feature test macros for C++23. */ cpp_define (pfile, "__cpp_size_t_suffix=202011L"); cpp_define (pfile, "__cpp_if_consteval=202106L"); - cpp_define (pfile, "__cpp_constexpr=202207L"); + cpp_define (pfile, "__cpp_constexpr=202211L"); cpp_define (pfile, "__cpp_multidimensional_subscript=202211L"); cpp_define (pfile, "__cpp_named_character_escapes=202207L"); cpp_define (pfile, "__cpp_static_call_operator=202207L"); --- gcc/cp/constexpr.cc.jj 2022-11-17 08:48:30.530357181 +0100 +++ gcc/cp/constexpr.cc 2022-11-17 20:53:15.432408015 +0100 @@ -7100,17 +7100,35 @@ cxx_eval_constant_expression (const cons /* Allow __FUNCTION__ etc. */ && !DECL_ARTIFICIAL (r)) { - if (!ctx->quiet) + bool ok = decl_constant_var_p (r); + /* Since P2647R1 control can pass through definitions of static + or thread_local vars usable in constant expressions. + In C++20 or older, if such vars are declared inside of + constexpr or consteval function, diagnose_static_in_constexpr + should have already pedwarned on those. Otherwise they could + be e.g. in a statement expression, reject those before + C++23. */ + if (ok && cxx_dialect < cxx23) { - if (CP_DECL_THREAD_LOCAL_P (r)) - error_at (loc, "control passes through definition of %qD " - "with thread storage duration", r); - else - error_at (loc, "control passes through definition of %qD " - "with static storage duration", r); + tree fnctx = decl_function_context (r); + if (fnctx == NULL_TREE + || !DECL_DECLARED_CONSTEXPR_P (fnctx)) + ok = false; + } + if (!ok) + { + if (!ctx->quiet) + { + if (CP_DECL_THREAD_LOCAL_P (r)) + error_at (loc, "control passes through definition of " + "%qD with thread storage duration", r); + else + error_at (loc, "control passes through definition of " + "%qD with static storage duration", r); + } + *non_constant_p = true; + break; } - *non_constant_p = true; - break; } if (AGGREGATE_TYPE_P (TREE_TYPE (r)) @@ -9588,21 +9606,41 @@ potential_constant_expression_1 (tree t, tmp = DECL_EXPR_DECL (t); if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp)) { - if (CP_DECL_THREAD_LOCAL_P (tmp) && !DECL_REALLY_EXTERN (tmp)) - { - if (flags & tf_error) - constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p, - "%qD defined %<thread_local%> in " - "%<constexpr%> context", tmp); - return false; - } - else if (TREE_STATIC (tmp)) + if (TREE_STATIC (tmp) + || (CP_DECL_THREAD_LOCAL_P (tmp) && !DECL_REALLY_EXTERN (tmp))) { - if (flags & tf_error) - constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p, - "%qD defined %<static%> in %<constexpr%> " - "context", tmp); - return false; + bool ok = (processing_template_decl + ? decl_maybe_constant_var_p (tmp) + : decl_constant_var_p (tmp)); + /* Since P2647R1 control can pass through definitions of static + or thread_local vars usable in constant expressions. + In C++20 or older, if such vars are declared inside of + constexpr or consteval function, diagnose_static_in_constexpr + should have already pedwarned on those. Otherwise they could + be e.g. in a statement expression, reject those before + C++23. */ + if (ok && cxx_dialect < cxx23) + { + tree fnctx = decl_function_context (tmp); + if (fnctx == NULL_TREE + || !DECL_DECLARED_CONSTEXPR_P (fnctx)) + ok = false; + } + if (!ok) + { + if (flags & tf_error) + { + if (CP_DECL_THREAD_LOCAL_P (tmp)) + constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p, + "%qD defined %<thread_local%> in " + "%<constexpr%> context", tmp); + else + constexpr_error (DECL_SOURCE_LOCATION (tmp), fundef_p, + "%qD defined %<static%> in " + "%<constexpr%> context", tmp); + } + return false; + } } else if (!check_for_uninitialized_const_var (tmp, /*constexpr_context_p=*/true, flags)) --- gcc/cp/decl.cc.jj 2022-11-16 14:44:43.692339668 +0100 +++ gcc/cp/decl.cc 2022-11-17 20:53:44.102011594 +0100 @@ -5600,6 +5600,57 @@ groktypename (cp_decl_specifier_seq *typ return type; } +/* For C++17 and older diagnose static or thread_local decls in constexpr + or consteval functions. For C++20 similarly, except if they are + usable in constant expressions. */ + +static void +diagnose_static_in_constexpr (tree decl) +{ + if (cxx_dialect >= cxx23) + return; + if (current_function_decl + && VAR_P (decl) + && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + { + bool ok = false; + if (processing_template_decl + ? decl_maybe_constant_var_p (decl) + : decl_constant_var_p (decl)) + { + if (CP_DECL_THREAD_LOCAL_P (decl) && !DECL_REALLY_EXTERN (decl)) + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wpedantic, + "%qD defined %<thread_local%> in %qs function only " + "available with %<-std=c++2b%> or %<-std=gnu++2b%>", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); + else if (TREE_STATIC (decl)) + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wpedantic, + "%qD defined %<static%> in %qs function only available " + "with %<-std=c++2b%> or %<-std=gnu++2b%>", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); + ok = true; + } + else if (CP_DECL_THREAD_LOCAL_P (decl) && !DECL_REALLY_EXTERN (decl)) + error_at (DECL_SOURCE_LOCATION (decl), + "%qD defined %<thread_local%> in %qs function only " + "available with %<-std=c++2b%> or %<-std=gnu++2b%>", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); + else if (TREE_STATIC (decl)) + error_at (DECL_SOURCE_LOCATION (decl), + "%qD defined %<static%> in %qs function only available " + "with %<-std=c++2b%> or %<-std=gnu++2b%>", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); + else + ok = true; + if (!ok) + cp_function_chain->invalid_constexpr = true; + } +} + /* Process a DECLARATOR for a function-scope or namespace-scope variable or function declaration. (Function definitions go through start_function; class member @@ -5860,29 +5911,6 @@ start_decl (const cp_declarator *declara DECL_THIS_STATIC (decl) = 1; } - if (current_function_decl && VAR_P (decl) - && DECL_DECLARED_CONSTEXPR_P (current_function_decl) - && cxx_dialect < cxx23) - { - bool ok = false; - if (CP_DECL_THREAD_LOCAL_P (decl) && !DECL_REALLY_EXTERN (decl)) - error_at (DECL_SOURCE_LOCATION (decl), - "%qD defined %<thread_local%> in %qs function only " - "available with %<-std=c++2b%> or %<-std=gnu++2b%>", decl, - DECL_IMMEDIATE_FUNCTION_P (current_function_decl) - ? "consteval" : "constexpr"); - else if (TREE_STATIC (decl)) - error_at (DECL_SOURCE_LOCATION (decl), - "%qD defined %<static%> in %qs function only available " - "with %<-std=c++2b%> or %<-std=gnu++2b%>", decl, - DECL_IMMEDIATE_FUNCTION_P (current_function_decl) - ? "consteval" : "constexpr"); - else - ok = true; - if (!ok) - cp_function_chain->invalid_constexpr = true; - } - if (!processing_template_decl && VAR_P (decl)) start_decl_1 (decl, initialized); @@ -8424,6 +8452,9 @@ cp_finish_decl (tree decl, tree init, bo set_user_assembler_name (decl, asmspec); DECL_HARD_REGISTER (decl) = 1; } + + diagnose_static_in_constexpr (decl); + return; } @@ -8749,6 +8780,8 @@ cp_finish_decl (tree decl, tree init, bo && !DECL_HARD_REGISTER (decl)) targetm.lower_local_decl_alignment (decl); + diagnose_static_in_constexpr (decl); + invoke_plugin_callbacks (PLUGIN_FINISH_DECL, decl); } --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit17.C.jj 2022-11-17 09:00:42.108248984 +0100 +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit17.C 2022-11-17 20:55:33.550498209 +0100 @@ -0,0 +1,12 @@ +// P2647R1 - Permitting static constexpr variables in constexpr functions +// { dg-do compile { target c++23 } } + +constexpr char +test () +{ + static const int x = 5; + static constexpr char c[] = "Hello World"; + return *(c + x); +} + +static_assert (test () == ' '); --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C.jj 2022-11-17 09:29:45.776136195 +0100 +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C 2022-11-17 21:02:20.852865509 +0100 @@ -0,0 +1,49 @@ +// P2647R1 - Permitting static constexpr variables in constexpr functions +// { dg-do compile { target c++14 } } + +constexpr int +f1 (int x) +{ + if (x) + throw 1; + return 0; +} + +constexpr int +f2 () +{ + static const int a = f1 (1); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int +f3 () +{ + static const int a = 5; // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int +f4 () // { dg-message "declared here" "" { target c++20_down } } +{ // { dg-message "is not usable as a 'constexpr' function because:" "" { target c++23 } .-1 } + static const int a = f1 (1); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } +} + +constexpr int a4 = f4 (); // { dg-error "called in a constant expression" } + +constexpr int +f5 () +{ + static const int a = f1 (0); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int +f6 () +{ + static const int a = f1 (0); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int a6 = f6 (); --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C.jj 2022-11-17 20:56:07.887023431 +0100 +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit19.C 2022-11-17 21:04:12.618319027 +0100 @@ -0,0 +1,50 @@ +// P2647R1 - Permitting static constexpr variables in constexpr functions +// { dg-do compile { target c++14 } } +// { dg-options "-pedantic" } + +constexpr int +f1 (int x) +{ + if (x) + throw 1; + return 0; +} + +constexpr int +f2 () +{ + static const int a = f1 (1); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int +f3 () +{ + static const int a = 5; // { dg-warning "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int +f4 () // { dg-message "declared here" "" { target c++20_down } } +{ // { dg-message "is not usable as a 'constexpr' function because:" "" { target c++23 } .-1 } + static const int a = f1 (1); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } +} + +constexpr int a4 = f4 (); // { dg-error "called in a constant expression" } + +constexpr int +f5 () +{ + static const int a = f1 (0); // { dg-warning "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int +f6 () +{ + static const int a = f1 (0); // { dg-warning "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int a6 = f6 (); --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit20.C.jj 2022-11-17 20:56:28.527738024 +0100 +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit20.C 2022-11-17 21:31:25.209729268 +0100 @@ -0,0 +1,50 @@ +// P2647R1 - Permitting static constexpr variables in constexpr functions +// { dg-do compile { target c++14 } } +// { dg-options "-Wno-pedantic" } + +constexpr int +f1 (int x) +{ + if (x) + throw 1; + return 0; +} + +constexpr int +f2 () +{ + static const int a = f1 (1); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +constexpr int +f3 () +{ + static const int a = 5; + return 0; +} + +constexpr int +f4 () // { dg-message "declared here" "" { target c++20_down } } +{ // { dg-message "is not usable as a 'constexpr' function because:" "" { target c++23 } .-1 } + static const int a = f1 (1); // { dg-error "'a' defined 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; // { dg-error "'a' defined 'static' in 'constexpr' context" "" { target c++23 } .-1 } +} + +constexpr int a4 = f4 (); // { dg-error "called in a constant expression" } + +constexpr int +f5 () +{ + static const int a = f1 (0); + return 0; +} + +constexpr int +f6 () +{ + static const int a = f1 (0); + return 0; +} + +constexpr int a6 = f6 (); --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2022-11-17 08:48:30.561356753 +0100 +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2022-11-17 09:00:42.108248984 +0100 @@ -134,8 +134,8 @@ #ifndef __cpp_constexpr # error "__cpp_constexpr" -#elif __cpp_constexpr != 202207 -# error "__cpp_constexpr != 202207" +#elif __cpp_constexpr != 202211 +# error "__cpp_constexpr != 202211" #endif #ifndef __cpp_decltype_auto --- gcc/testsuite/g++.dg/ext/stmtexpr19.C.jj 2022-11-17 08:48:02.730741221 +0100 +++ gcc/testsuite/g++.dg/ext/stmtexpr19.C 2022-11-17 20:57:06.936206927 +0100 @@ -8,7 +8,7 @@ const test* setup() { static constexpr test atest = { - ({ static const int inner = 123; &inner; }) // { dg-error "static" } + ({ static const int inner = 123; &inner; }) // { dg-error "static" "" { target c++20_down } } }; return &atest; Jakub