Message ID | Y3acqbULfy3PULmc@tucnak |
---|---|
State | New |
Headers | show |
Series | c++, v4: Implement C++23 P2647R1 - Permitting static constexpr variables in constexpr functions | expand |
On Thu, Nov 17, 2022 at 09:42:17PM +0100, Jakub Jelinek wrote: > 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 <jakub@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; FWIW, I couldn't find a way to trigger this code. > + } > + 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 In C++17 we don't support consteval so I guess drop the "or consteval "? BTW, I notice that the patch breaks g++.dg/cpp1y/lambda-generic-func1.C g++.dg/cpp1z/constexpr-lambda16.C Maybe they just need dg- tweaks. Marek
On 11/17/22 15:42, Jakub Jelinek wrote: > 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. We could, but I wouldn't bother to enforce this specially for statement-expressions, which are already an extension. OTOH, we should test that static constexpr is handled properly for lambdas, i.e. this should still fail: constexpr int q = [](int i) { static constexpr int x = 42; return x+i; }(24); > 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 <jakub@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 >
On Thu, Nov 17, 2022 at 07:15:05PM -0500, Marek Polacek wrote: > > --- 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 > > In C++17 we don't support consteval so I guess drop the "or consteval "? I just forgot to update the function comment. Anyway, I think: > BTW, I notice that the patch breaks > g++.dg/cpp1y/lambda-generic-func1.C > g++.dg/cpp1z/constexpr-lambda16.C > Maybe they just need dg- tweaks. this is actually a real bug and I'm not sure how to resolve that. We have there: int main() { [](auto i) { if (i) { int j; static int k; return i + j; } return i; }(0); } and for C++17/20 I presume something (haven't figured out yet what) marks the lambda operator() when still a template as constexpr and then cp_finish_decl -> diagnose_static_in_constexpr pedwarns on it. For the above perhaps we could figure out there is a static int k; in the operator() and don't turn it into constexpr, but what if there is something that would e.g. satisfy decl_maybe_constant_var_p but not decl_constant_var_p when actually instantiated? Without my patch, the diagnostics is in start_decl which isn't called again during instantiation, so I presume we mark it as constexpr and then we'd diagnose it during constant evaluation. Jakub
On Fri, Nov 18, 2022 at 08:48:32AM +0100, Jakub Jelinek wrote: > On Thu, Nov 17, 2022 at 07:15:05PM -0500, Marek Polacek wrote: > > > --- 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 > > > > In C++17 we don't support consteval so I guess drop the "or consteval "? > > I just forgot to update the function comment. > > Anyway, I think: > > > BTW, I notice that the patch breaks > > g++.dg/cpp1y/lambda-generic-func1.C > > g++.dg/cpp1z/constexpr-lambda16.C > > Maybe they just need dg- tweaks. > > this is actually a real bug and I'm not sure how to resolve that. > > We have there: > > int main() > { > [](auto i) { if (i) { int j; static int k; return i + j; } return i; }(0); > } > > and for C++17/20 I presume something (haven't figured out yet what) marks Right, that's the C++17 implicit constexpr for lambdas, finish_function: /* Lambda closure members are implicitly constexpr if possible. */ if (cxx_dialect >= cxx17 && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))) DECL_DECLARED_CONSTEXPR_P (fndecl) = ((processing_template_decl || is_valid_constexpr_fn (fndecl, /*complain*/false)) && potential_constant_expression (DECL_SAVED_TREE (fndecl))); > the lambda operator() when still a template as constexpr and then > cp_finish_decl -> diagnose_static_in_constexpr pedwarns on it. > For the above perhaps we could figure out there is a static int k; in the > operator() and don't turn it into constexpr, but what if there is > something that would e.g. satisfy decl_maybe_constant_var_p but not > decl_constant_var_p when actually instantiated? > Without my patch, the diagnostics is in start_decl which isn't called again > during instantiation, so I presume we mark it as constexpr and then we'd > diagnose it during constant evaluation. Um, can we give up on trying to handle C++17/C++20 then? Marek
On Fri, Nov 18, 2022 at 10:03:18AM -0500, Marek Polacek wrote: > > the lambda operator() when still a template as constexpr and then > > cp_finish_decl -> diagnose_static_in_constexpr pedwarns on it. > > For the above perhaps we could figure out there is a static int k; in the > > operator() and don't turn it into constexpr, but what if there is > > something that would e.g. satisfy decl_maybe_constant_var_p but not > > decl_constant_var_p when actually instantiated? > > Without my patch, the diagnostics is in start_decl which isn't called again > > during instantiation, so I presume we mark it as constexpr and then we'd > > diagnose it during constant evaluation. > > Um, can we give up on trying to handle C++17/C++20 then? That was why I've posted the other two variant patches (with the 3rd one being a strict C++23 only change). Even if it is just a temporary state, make C++23 work first and then iterate if it is possible to make C++17/20 working with the pedwarns right. Jakub
On 11/18/22 10:03, Marek Polacek wrote: > On Fri, Nov 18, 2022 at 08:48:32AM +0100, Jakub Jelinek wrote: >> On Thu, Nov 17, 2022 at 07:15:05PM -0500, Marek Polacek wrote: >>>> --- 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 >>> >>> In C++17 we don't support consteval so I guess drop the "or consteval "? >> >> I just forgot to update the function comment. >> >> Anyway, I think: >> >>> BTW, I notice that the patch breaks >>> g++.dg/cpp1y/lambda-generic-func1.C >>> g++.dg/cpp1z/constexpr-lambda16.C >>> Maybe they just need dg- tweaks. >> >> this is actually a real bug and I'm not sure how to resolve that. >> >> We have there: >> >> int main() >> { >> [](auto i) { if (i) { int j; static int k; return i + j; } return i; }(0); >> } >> >> and for C++17/20 I presume something (haven't figured out yet what) marks > > Right, that's the C++17 implicit constexpr for lambdas, finish_function: > > /* Lambda closure members are implicitly constexpr if possible. */ > if (cxx_dialect >= cxx17 > && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))) > DECL_DECLARED_CONSTEXPR_P (fndecl) > = ((processing_template_decl > || is_valid_constexpr_fn (fndecl, /*complain*/false)) > && potential_constant_expression (DECL_SAVED_TREE (fndecl))); Yeah, I guess potential_constant_expression needs to be stricter in a lambda. Or perhaps any function that isn't already DECL_DECLARED_CONSTEXPR_P? >> the lambda operator() when still a template as constexpr and then >> cp_finish_decl -> diagnose_static_in_constexpr pedwarns on it. >> For the above perhaps we could figure out there is a static int k; in the >> operator() and don't turn it into constexpr, but what if there is >> something that would e.g. satisfy decl_maybe_constant_var_p but not >> decl_constant_var_p when actually instantiated? I'd expect the above change to potential_c_e to handle that case. Jason
On Fri, Nov 18, 2022 at 11:24:45AM -0500, Jason Merrill wrote: > > Right, that's the C++17 implicit constexpr for lambdas, finish_function: > > > > /* Lambda closure members are implicitly constexpr if possible. */ > > if (cxx_dialect >= cxx17 > > && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))) > > DECL_DECLARED_CONSTEXPR_P (fndecl) > > = ((processing_template_decl > > || is_valid_constexpr_fn (fndecl, /*complain*/false)) > > && potential_constant_expression (DECL_SAVED_TREE (fndecl))); > > Yeah, I guess potential_constant_expression needs to be stricter in a > lambda. Or perhaps any function that isn't already > DECL_DECLARED_CONSTEXPR_P? potential_constant_expression can't be relied on that it catches up everything if it, even a simple if statement with a condition not yet known to be 0 or non-0 results in just a requirement that at least one of the substatements is potential constant, etc. Similarly switch statements etc. If there is a way to distinguish between functions with user specified constexpr/consteval and DECL_DECLARED_CONSTEXPR_P set through the above if condition, sure, cp_finish_decl -> check_static_in_constexpr could be perhaps silent about those, but then we want to diagnose it during constexpr evaluation at least. But in that case having it a pedwarn rather than "this is a constant expression" vs. "this is not a constant expression, if !ctx->quiet emit an error" is something I don't see how to handle. Because something needs to be returned, it is a constant expression or it is not. Jakub
On 11/18/22 11:34, Jakub Jelinek wrote: > On Fri, Nov 18, 2022 at 11:24:45AM -0500, Jason Merrill wrote: >>> Right, that's the C++17 implicit constexpr for lambdas, finish_function: >>> >>> /* Lambda closure members are implicitly constexpr if possible. */ >>> if (cxx_dialect >= cxx17 >>> && LAMBDA_TYPE_P (CP_DECL_CONTEXT (fndecl))) >>> DECL_DECLARED_CONSTEXPR_P (fndecl) >>> = ((processing_template_decl >>> || is_valid_constexpr_fn (fndecl, /*complain*/false)) >>> && potential_constant_expression (DECL_SAVED_TREE (fndecl))); >> >> Yeah, I guess potential_constant_expression needs to be stricter in a >> lambda. Or perhaps any function that isn't already >> DECL_DECLARED_CONSTEXPR_P? > > potential_constant_expression can't be relied on that it catches up > everything if it, even a simple if statement with a condition not yet > known to be 0 or non-0 results in just a requirement that at least > one of the substatements is potential constant, etc. > Similarly switch statements etc. > If there is a way to distinguish between functions with user > specified constexpr/consteval and DECL_DECLARED_CONSTEXPR_P set > through the above if condition, sure, cp_finish_decl -> > check_static_in_constexpr could be perhaps silent about those, but then > we want to diagnose it during constexpr evaluation at least. But in that > case having it a pedwarn rather than "this is a constant expression" > vs. "this is not a constant expression, if !ctx->quiet emit an error" > is something I don't see how to handle. Because something needs > to be returned, it is a constant expression or it is not. True. Let's go with your option 2, then, thanks. Jason
--- 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;