Message ID | 20211005123910.GW304296@tucnak |
---|---|
State | New |
Headers | show |
Series | c++: Implement C++23 P2242R3 - Non-literal variables (and labels and gotos) in constexpr functions | expand |
On 10/5/21 08:39, Jakub Jelinek wrote: > Hi! > > The following patch implements C++23 P2242R3 - Non-literal variables > (and labels and gotos) in constexpr functions. > I think it is mostly straightforward, don't diagnose certain > statements/declarations just because of their presence in > constexpr/consteval functions, but (except for the non-literal type > var declarations which ought to be caught by e.g. constructor or > destructor call during evaluation not being constexpr and for > labels which are now always allowed) diagnose it during constexpr > evaluation. > > The only unclear thing to me is "a control flow that passes through a > declaration". The patch implements diagnostics when encountering a > DECL_EXPR for such variables. But because constexpr/consteval functions > support switch statements, another thing that can happen is jump over > such a declaration. Consider e.g. following testcase (not included > in the patch). When evaluating foo (12) or bar (12) the patch > accepts those as constant expressions, eventhough it jumps across such > declarations. For baz (12) and corge (12) it rejects them not > because of jumping across such declarations, but because those > static or thread_local variables aren't initialized with constant > expressions and are accessed. If baz/corge is modified not to have > const keyword, they are rejected because the vars aren't aren't const. > If they have const keyword and are initialized by constant expression, > accesses to those vars are accepted. Is that ok? That seems correct to me. If they have static initialization, they are initialized. Some comments in the patch below. > So far regtested with > GXX_TESTSUITE_STDS=98,11,14,17,20,2b make -j32 -k check-g++ > ok for trunk if it passes full bootstrap/regtest? > > // { dg-do compile } > // { dg-options "-std=c++2b" } > > int qux (); > > constexpr int > foo (int x) > { > switch (x) > { > static int bar = qux (); > case 12: > return 1; > } > return 0; > } > > constexpr int > bar (int x) > { > switch (x) > { > thread_local int bar = qux (); > case 12: > return 1; > } > return 0; > } > > constexpr int > baz (int x) > { > switch (x) > { > static const int bar = qux (); // { dg-message "'bar' was not initialized with a constant expression" } > case 12: > return bar; > } > return 0; > } > > constexpr int > corge (int x) > { > switch (x) > { > const thread_local int bar = qux (); // { dg-message "'bar' was not initialized with a constant expression" } > case 12: > return bar; > } > return 0; > } > > constexpr int a = foo (12); > constexpr int b = bar (12); > constexpr int c = baz (12); // { dg-error "the value of 'bar' is not usable in a constant expression" } > constexpr int d = corge (12); // { dg-error "the value of 'bar' is not usable in a constant expression" } > > > 2021-10-05 Jakub Jelinek <jakub@redhat.com> > > gcc/c-family/ > * c-cppbuiltin.c (c_cpp_builtins): For -std=c++23 predefine > __cpp_constexpr to 202103L rather than 201907L. > gcc/cp/ > * parser.c (cp_parser_jump_statement): Implement C++23 P2242R3. > Allow goto expressions in constexpr function bodies for C++23. > Adjust error message for older standards to mention it. > * decl.c (start_decl): Allow static and thread_local declarations > in constexpr function bodies for C++23. Adjust error message for > older standards to mention it. > * constexpr.c (ensure_literal_type_for_constexpr_object): Allow > declarations of variables with non-literal type in constexpr function > bodies for C++23. Adjust error message for older standards to mention > it. > (cxx_eval_constant_expression) <case DECL_EXPR>: Diagnose declarations > of initialization of static or thread_local vars. > (cxx_eval_constant_expression) <case GOTO_EXPR>: Diagnose goto > statements for C++23. > (potential_constant_expression_1) <case DECL_EXPR>: Allow declarations > of static and thread_local vars for C++23. > (potential_constant_expression_1) <case LABEL_EXPR>: Allow labels for > C++23. > gcc/testsuite/ > * g++.dg/cpp23/feat-cxx2b.C: Expect __cpp_constexpr 202103L rather > than 201907L. > * g++.dg/cpp23/constexpr-nonlit1.C: New test. > * g++.dg/cpp23/constexpr-nonlit2.C: New test. > * g++.dg/cpp23/constexpr-nonlit3.C: New test. > * g++.dg/diagnostic/constexpr1.C: Only expect some diagnostics for > c++20_down. > * g++.dg/cpp1y/constexpr-label.C: Likewise. > * g++.dg/cpp1y/constexpr-neg1.C: Likewise. > * g++.dg/cpp2a/constexpr-try5.C: Likewise. Adjust some expected > wording. > * g++.dg/cpp2a/constexpr-dtor3.C: Likewise. > * g++.dg/cpp2a/consteval3.C: Likewise. Add effective target c++20 > and remove dg-options. > > --- gcc/c-family/c-cppbuiltin.c.jj 2021-09-21 23:31:01.016248936 +0200 > +++ gcc/c-family/c-cppbuiltin.c 2021-10-05 12:54:29.898321379 +0200 > @@ -1052,7 +1052,8 @@ c_cpp_builtins (cpp_reader *pfile) > cpp_define (pfile, "__cpp_init_captures=201803L"); > cpp_define (pfile, "__cpp_generic_lambdas=201707L"); > cpp_define (pfile, "__cpp_designated_initializers=201707L"); > - cpp_define (pfile, "__cpp_constexpr=201907L"); > + if (cxx_dialect <= cxx20) > + cpp_define (pfile, "__cpp_constexpr=201907L"); > cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L"); > cpp_define (pfile, "__cpp_conditional_explicit=201806L"); > cpp_define (pfile, "__cpp_consteval=201811L"); > @@ -1071,6 +1072,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=202103L"); > } > if (flag_concepts) > { > --- gcc/cp/parser.c.jj 2021-10-01 18:47:58.090795589 +0200 > +++ gcc/cp/parser.c 2021-10-05 12:54:29.907321254 +0200 > @@ -14176,9 +14176,11 @@ cp_parser_jump_statement (cp_parser* par > > case RID_GOTO: > if (parser->in_function_body > - && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) > + && DECL_DECLARED_CONSTEXPR_P (current_function_decl) > + && cxx_dialect < cxx23) > { > - error ("%<goto%> in %<constexpr%> function"); > + error ("%<goto%> in %<constexpr%> function only available with " > + "%<-std=c++2b%> or %<-std=gnu++2b%>"); > cp_function_chain->invalid_constexpr = true; > } > > --- gcc/cp/decl.c.jj 2021-09-21 23:31:01.042248567 +0200 > +++ gcc/cp/decl.c 2021-10-05 12:54:29.909321226 +0200 > @@ -5709,17 +5709,20 @@ start_decl (const cp_declarator *declara > } > > if (current_function_decl && VAR_P (decl) > - && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) > + && DECL_DECLARED_CONSTEXPR_P (current_function_decl) > + && cxx_dialect < cxx23) > { > bool ok = false; > if (CP_DECL_THREAD_LOCAL_P (decl)) > error_at (DECL_SOURCE_LOCATION (decl), > - "%qD declared %<thread_local%> in %qs function", decl, > + "%qD declared %<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 declared %<static%> in %qs function", decl, > + "%qD declared %<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 > --- gcc/cp/constexpr.c.jj 2021-09-22 23:19:37.498912776 +0200 > +++ gcc/cp/constexpr.c 2021-10-05 13:56:02.377831320 +0200 > @@ -109,14 +109,15 @@ ensure_literal_type_for_constexpr_object > explain_non_literal_class (type); > decl = error_mark_node; > } > - else > + else if (cxx_dialect < cxx23) > { > if (!is_instantiation_of_constexpr (current_function_decl)) > { > auto_diagnostic_group d; > error_at (DECL_SOURCE_LOCATION (decl), > - "variable %qD of non-literal type %qT in " > - "%<constexpr%> function", decl, type); > + "variable %qD of non-literal type %qT in " > + "%<constexpr%> function only available with " > + "%<-std=c++2b%> or %<-std=gnu++2b%>", decl, type); > explain_non_literal_class (type); > decl = error_mark_node; > } > @@ -6345,6 +6346,26 @@ cxx_eval_constant_expression (const cons > r = void_node; > break; > } > + > + if (VAR_P (r) > + && (TREE_STATIC (r) || CP_DECL_THREAD_LOCAL_P (r)) > + /* Allow __FUNCTION__ etc. */ > + && !DECL_ARTIFICIAL (r)) > + { > + gcc_assert (cxx_dialect >= cxx23); > + if (!ctx->quiet) > + { > + if (CP_DECL_THREAD_LOCAL_P (r)) > + error_at (loc, "control passes through declaration of %qD " > + "with thread storage duration", r); > + else > + error_at (loc, "control passes through declaration of %qD " > + "with static storage duration", r); > + } > + *non_constant_p = true; > + break; > + } > + > if (AGGREGATE_TYPE_P (TREE_TYPE (r)) > || VECTOR_TYPE_P (TREE_TYPE (r))) > { > @@ -7049,10 +7070,18 @@ cxx_eval_constant_expression (const cons > break; > > case GOTO_EXPR: > - *jump_target = TREE_OPERAND (t, 0); > - gcc_assert (breaks (jump_target) || continues (jump_target) > - /* Allow for jumping to a cdtor_label. */ > - || returns (jump_target)); > + if (breaks (&TREE_OPERAND (t, 0)) > + || continues (&TREE_OPERAND (t, 0)) > + /* Allow for jumping to a cdtor_label. */ > + || returns (&TREE_OPERAND (t, 0))) > + *jump_target = TREE_OPERAND (t, 0); > + else > + { > + gcc_assert (cxx_dialect >= cxx23); > + if (!ctx->quiet) > + error_at (loc, "%<goto%> is not a constant expression"); > + *non_constant_p = true; > + } > break; > > case LOOP_EXPR: > @@ -8736,14 +8765,14 @@ potential_constant_expression_1 (tree t, > tmp = DECL_EXPR_DECL (t); > if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp)) > { > - if (TREE_STATIC (tmp)) > + if (TREE_STATIC (tmp) && cxx_dialect < cxx23) > { > if (flags & tf_error) > error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared " > "%<static%> in %<constexpr%> context", tmp); > return false; > } > - else if (CP_DECL_THREAD_LOCAL_P (tmp)) > + else if (CP_DECL_THREAD_LOCAL_P (tmp) && cxx_dialect < cxx23) > { > if (flags & tf_error) > error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared " Hmm, I wouldn't expect this hunk to be needed; if there's a control path that doesn't include these declarations, we shouldn't hit these errors. > @@ -9025,7 +9054,7 @@ potential_constant_expression_1 (tree t, > > case LABEL_EXPR: > t = LABEL_EXPR_LABEL (t); > - if (DECL_ARTIFICIAL (t)) > + if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23) > return true; > else if (flags & tf_error) > error_at (loc, "label definition is not a constant expression"); Let's change this message to "only available with..." like the others. > --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2021-06-11 23:44:55.883676082 +0200 > +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2021-10-05 12:54:29.910321212 +0200 > @@ -134,8 +134,8 @@ > > #ifndef __cpp_constexpr > # error "__cpp_constexpr" > -#elif __cpp_constexpr != 201907 > -# error "__cpp_constexpr != 201907" > +#elif __cpp_constexpr != 202103 > +# error "__cpp_constexpr != 202103" > #endif > > #ifndef __cpp_decltype_auto > --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C.jj 2021-10-05 12:54:29.910321212 +0200 > +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C 2021-10-05 13:57:09.582896213 +0200 > @@ -0,0 +1,68 @@ > +// P2242R3 > +// { dg-do compile { target c++14 } } > + > +constexpr int > +foo () > +{ > +lab: // { dg-error "label definition is not a constant expression" "" { target c++20_down } } > + return 1; > +} > + > +constexpr int > +bar (int x) > +{ > + if (x) > + goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > + return 1; > +lab: > + return 0; > +} > + > +constexpr int > +baz (int x) > +{ > + if (!x) > + return 1; > + static int a; // { dg-error "'a' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } > + return ++a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 } > +} > + > +constexpr int > +qux (int x) > +{ > + if (!x) > + return 1; > + thread_local int a; // { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } } > + return ++a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 } > +} > + > +constexpr int > +garply (int x) > +{ > + if (!x) > + return 1; > + extern thread_local int a; // { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } } > + return ++a; > +} > + > +struct S { S (); ~S (); int s; }; // { dg-message "'S' is not literal because:" "" { target c++20_down } } > + // { dg-message "'S' has a non-trivial destructor" "" { target c++17_down } .-1 } > + // { dg-message "'S' does not have 'constexpr' destructor" "" { target { c++20_only } } .-2 } > + > +constexpr int > +corge (int x) > +{ > + if (!x) > + return 1; > + S s; // { dg-error "variable 's' of non-literal type 'S' in 'constexpr' function only available with" "" { target c++20_down } } > + return 0; > +} > + > +#if __cpp_constexpr >= 202103L > +static_assert (foo ()); > +static_assert (bar (0)); > +static_assert (baz (0)); > +static_assert (qux (0)); > +static_assert (garply (0)); > +static_assert (corge (0)); > +#endif > --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C.jj 2021-10-05 12:54:29.910321212 +0200 > +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C 2021-10-05 12:54:29.910321212 +0200 > @@ -0,0 +1,64 @@ > +// P2242R3 > +// { dg-do compile } > +// { dg-options "-std=c++2b" } > + > +constexpr int > +foo () > +{ > +lab: > + return 1; > +} > + > +constexpr int > +bar (int x) > +{ > + if (x) > + goto lab; // { dg-error "'goto' is not a constant expression" } > + return 1; > +lab: > + return 0; > +} > + > +constexpr int > +baz (int x) > +{ > + if (!x) > + return 1; > + static int a; // { dg-error "control passes through declaration of 'a' with static storage duration" } > + return ++a; > +} > + > +constexpr int > +qux (int x) > +{ > + if (!x) > + return 1; > + thread_local int a; // { dg-error "control passes through declaration of 'a' with thread storage duration" } > + return ++a; > +} > + > +constexpr int > +garply (int x) > +{ > + if (!x) > + return 1; > + thread_local int a; // { dg-error "control passes through declaration of 'a' with thread storage duration" } > + return ++a; > +} > + > +struct S { S (); ~S (); int s; }; // { dg-message "'S::S\\\(\\\)' declared here" } > + > +constexpr int > +corge (int x) > +{ > + if (!x) > + return 1; > + S s; // { dg-error "call to non-'constexpr' function 'S::S\\\(\\\)'" } > + return 0; > +} > + > +constexpr int a = bar (1); // { dg-message "in 'constexpr' expansion of" } > +constexpr int b = baz (1); // { dg-message "in 'constexpr' expansion of" } > +constexpr int c = qux (1); // { dg-message "in 'constexpr' expansion of" } > +constexpr int d = garply (1); // { dg-message "in 'constexpr' expansion of" } > +constexpr int e = corge (1); // { dg-message "in 'constexpr' expansion of" } > --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C.jj 2021-10-05 12:54:29.910321212 +0200 > +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C 2021-10-05 13:57:21.176734894 +0200 > @@ -0,0 +1,10 @@ > +// P2242R3 > +// { dg-do compile { target c++14 } } > + > +constexpr int > +foo () > +{ > + goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > +lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } > + return 1; > +} > --- gcc/testsuite/g++.dg/diagnostic/constexpr1.C.jj 2020-01-14 20:02:46.815609354 +0100 > +++ gcc/testsuite/g++.dg/diagnostic/constexpr1.C 2021-10-05 13:56:30.249443508 +0200 > @@ -1,5 +1,5 @@ > // { dg-do compile { target c++11 } } > > -constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. declared .thread_local." } > +constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. declared .thread_local." "" { target c++20_down } } > > -constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. declared .static." } > +constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. declared .static." "" { target c++20_down } } > --- gcc/testsuite/g++.dg/cpp1y/constexpr-label.C.jj 2020-11-22 23:21:35.866434202 +0100 > +++ gcc/testsuite/g++.dg/cpp1y/constexpr-label.C 2021-10-05 13:37:37.660219970 +0200 > @@ -4,6 +4,6 @@ > constexpr int > f () > { > -x: // { dg-error "label definition is not a constant expression" } > +x: // { dg-error "label definition is not a constant expression" "" { target c++20_down } } > return 42; > } > --- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C.jj 2020-01-14 20:02:46.773609984 +0100 > +++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C 2021-10-05 13:38:23.322583787 +0200 > @@ -3,13 +3,13 @@ > struct A { A(); }; > > constexpr int f(int i) { > - static int j = i; // { dg-error "static" } > - thread_local int l = i; // { dg-error "thread_local" } > + static int j = i; // { dg-error "static" "" { target c++20_down } } > + thread_local int l = i; // { dg-error "thread_local" "" { target c++20_down } } > goto foo; // { dg-error "goto" } > foo: > asm("foo"); // { dg-error "asm" "" { target c++17_down } } > int k; // { dg-error "uninitialized" "" { target c++17_down } } > - A a; // { dg-error "non-literal" } > + A a; // { dg-error "non-literal" "" { target c++20_down } } > return i; > } > > --- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C.jj 2020-01-14 20:02:46.000000000 +0100 > +++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C 2021-10-05 14:05:21.092057240 +0200 > @@ -5,14 +5,14 @@ > constexpr int foo () > try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > - static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" } > - goto l; // { dg-error "'goto' in 'constexpr' function" } > - l:; > + static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } > + goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > + l:; // { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 } > return 0; > } catch (...) { > long int c; // { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } } > - static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" } > - goto l2; // { dg-error "'goto' in 'constexpr' function" } > + static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } > + goto l2; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l2:; > return -1; > } > @@ -20,20 +20,20 @@ try { // { dg-warning "function-try-bl > constexpr int bar () > { > int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } > - static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" } > - goto l; // { dg-error "'goto' in 'constexpr' function" } > - l:; > + static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } > + goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > + l:; // { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 } > try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } > short c; // { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } } > - static float d; // { dg-error "'d' declared 'static' in 'constexpr' function" } > + static float d; // { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } > // { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 } > - goto l2; // { dg-error "'goto' in 'constexpr' function" } > + goto l2; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l2:; > return 0; > } catch (int) { > char e; // { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } } > - static int f = 5; // { dg-error "'f' declared 'static' in 'constexpr' function" } > - goto l3; // { dg-error "'goto' in 'constexpr' function" } > + static int f = 5; // { dg-error "'f' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } > + goto l3; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } > l3:; > return 1; > } > --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj 2021-04-14 19:19:14.049804260 +0200 > +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C 2021-10-05 13:40:48.282564167 +0200 > @@ -180,6 +180,6 @@ f7 () > constexpr int > f8 () > { > - T t4; // { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" } > + T t4; // { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function only available with" "" { target c++20_down } } > return 0; > } > --- gcc/testsuite/g++.dg/cpp2a/consteval3.C.jj 2020-02-28 23:21:00.540567921 +0100 > +++ gcc/testsuite/g++.dg/cpp2a/consteval3.C 2021-10-05 13:42:17.371322962 +0200 > @@ -1,5 +1,4 @@ > -// { dg-do compile } > -// { dg-options "-std=c++2a" } > +// { dg-do compile { target c++20 } } > > struct S { S () : a (0), b (1) {} int a, b; }; > int f1 (); // { dg-message "previous declaration 'int f1\\(\\)'" } > @@ -57,7 +56,7 @@ template consteval float f12 (float x); > consteval int > f13 (int x) > { > - static int a = 5; // { dg-error "'a' declared 'static' in 'consteval' function" } > - thread_local int b = 6; // { dg-error "'b' declared 'thread_local' in 'consteval' function" } > + static int a = 5; // { dg-error "'a' declared 'static' in 'consteval' function only available with" "" { target c++20_only } } > + thread_local int b = 6; // { dg-error "'b' declared 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } > return x; > } > > Jakub >
--- gcc/c-family/c-cppbuiltin.c.jj 2021-09-21 23:31:01.016248936 +0200 +++ gcc/c-family/c-cppbuiltin.c 2021-10-05 12:54:29.898321379 +0200 @@ -1052,7 +1052,8 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_init_captures=201803L"); cpp_define (pfile, "__cpp_generic_lambdas=201707L"); cpp_define (pfile, "__cpp_designated_initializers=201707L"); - cpp_define (pfile, "__cpp_constexpr=201907L"); + if (cxx_dialect <= cxx20) + cpp_define (pfile, "__cpp_constexpr=201907L"); cpp_define (pfile, "__cpp_constexpr_in_decltype=201711L"); cpp_define (pfile, "__cpp_conditional_explicit=201806L"); cpp_define (pfile, "__cpp_consteval=201811L"); @@ -1071,6 +1072,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=202103L"); } if (flag_concepts) { --- gcc/cp/parser.c.jj 2021-10-01 18:47:58.090795589 +0200 +++ gcc/cp/parser.c 2021-10-05 12:54:29.907321254 +0200 @@ -14176,9 +14176,11 @@ cp_parser_jump_statement (cp_parser* par case RID_GOTO: if (parser->in_function_body - && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + && DECL_DECLARED_CONSTEXPR_P (current_function_decl) + && cxx_dialect < cxx23) { - error ("%<goto%> in %<constexpr%> function"); + error ("%<goto%> in %<constexpr%> function only available with " + "%<-std=c++2b%> or %<-std=gnu++2b%>"); cp_function_chain->invalid_constexpr = true; } --- gcc/cp/decl.c.jj 2021-09-21 23:31:01.042248567 +0200 +++ gcc/cp/decl.c 2021-10-05 12:54:29.909321226 +0200 @@ -5709,17 +5709,20 @@ start_decl (const cp_declarator *declara } if (current_function_decl && VAR_P (decl) - && DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + && DECL_DECLARED_CONSTEXPR_P (current_function_decl) + && cxx_dialect < cxx23) { bool ok = false; if (CP_DECL_THREAD_LOCAL_P (decl)) error_at (DECL_SOURCE_LOCATION (decl), - "%qD declared %<thread_local%> in %qs function", decl, + "%qD declared %<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 declared %<static%> in %qs function", decl, + "%qD declared %<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 --- gcc/cp/constexpr.c.jj 2021-09-22 23:19:37.498912776 +0200 +++ gcc/cp/constexpr.c 2021-10-05 13:56:02.377831320 +0200 @@ -109,14 +109,15 @@ ensure_literal_type_for_constexpr_object explain_non_literal_class (type); decl = error_mark_node; } - else + else if (cxx_dialect < cxx23) { if (!is_instantiation_of_constexpr (current_function_decl)) { auto_diagnostic_group d; error_at (DECL_SOURCE_LOCATION (decl), - "variable %qD of non-literal type %qT in " - "%<constexpr%> function", decl, type); + "variable %qD of non-literal type %qT in " + "%<constexpr%> function only available with " + "%<-std=c++2b%> or %<-std=gnu++2b%>", decl, type); explain_non_literal_class (type); decl = error_mark_node; } @@ -6345,6 +6346,26 @@ cxx_eval_constant_expression (const cons r = void_node; break; } + + if (VAR_P (r) + && (TREE_STATIC (r) || CP_DECL_THREAD_LOCAL_P (r)) + /* Allow __FUNCTION__ etc. */ + && !DECL_ARTIFICIAL (r)) + { + gcc_assert (cxx_dialect >= cxx23); + if (!ctx->quiet) + { + if (CP_DECL_THREAD_LOCAL_P (r)) + error_at (loc, "control passes through declaration of %qD " + "with thread storage duration", r); + else + error_at (loc, "control passes through declaration of %qD " + "with static storage duration", r); + } + *non_constant_p = true; + break; + } + if (AGGREGATE_TYPE_P (TREE_TYPE (r)) || VECTOR_TYPE_P (TREE_TYPE (r))) { @@ -7049,10 +7070,18 @@ cxx_eval_constant_expression (const cons break; case GOTO_EXPR: - *jump_target = TREE_OPERAND (t, 0); - gcc_assert (breaks (jump_target) || continues (jump_target) - /* Allow for jumping to a cdtor_label. */ - || returns (jump_target)); + if (breaks (&TREE_OPERAND (t, 0)) + || continues (&TREE_OPERAND (t, 0)) + /* Allow for jumping to a cdtor_label. */ + || returns (&TREE_OPERAND (t, 0))) + *jump_target = TREE_OPERAND (t, 0); + else + { + gcc_assert (cxx_dialect >= cxx23); + if (!ctx->quiet) + error_at (loc, "%<goto%> is not a constant expression"); + *non_constant_p = true; + } break; case LOOP_EXPR: @@ -8736,14 +8765,14 @@ potential_constant_expression_1 (tree t, tmp = DECL_EXPR_DECL (t); if (VAR_P (tmp) && !DECL_ARTIFICIAL (tmp)) { - if (TREE_STATIC (tmp)) + if (TREE_STATIC (tmp) && cxx_dialect < cxx23) { if (flags & tf_error) error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared " "%<static%> in %<constexpr%> context", tmp); return false; } - else if (CP_DECL_THREAD_LOCAL_P (tmp)) + else if (CP_DECL_THREAD_LOCAL_P (tmp) && cxx_dialect < cxx23) { if (flags & tf_error) error_at (DECL_SOURCE_LOCATION (tmp), "%qD declared " @@ -9025,7 +9054,7 @@ potential_constant_expression_1 (tree t, case LABEL_EXPR: t = LABEL_EXPR_LABEL (t); - if (DECL_ARTIFICIAL (t)) + if (DECL_ARTIFICIAL (t) || cxx_dialect >= cxx23) return true; else if (flags & tf_error) error_at (loc, "label definition is not a constant expression"); --- gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C.jj 2021-06-11 23:44:55.883676082 +0200 +++ gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C 2021-10-05 12:54:29.910321212 +0200 @@ -134,8 +134,8 @@ #ifndef __cpp_constexpr # error "__cpp_constexpr" -#elif __cpp_constexpr != 201907 -# error "__cpp_constexpr != 201907" +#elif __cpp_constexpr != 202103 +# error "__cpp_constexpr != 202103" #endif #ifndef __cpp_decltype_auto --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C.jj 2021-10-05 12:54:29.910321212 +0200 +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit1.C 2021-10-05 13:57:09.582896213 +0200 @@ -0,0 +1,68 @@ +// P2242R3 +// { dg-do compile { target c++14 } } + +constexpr int +foo () +{ +lab: // { dg-error "label definition is not a constant expression" "" { target c++20_down } } + return 1; +} + +constexpr int +bar (int x) +{ + if (x) + goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } + return 1; +lab: + return 0; +} + +constexpr int +baz (int x) +{ + if (!x) + return 1; + static int a; // { dg-error "'a' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } + return ++a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 } +} + +constexpr int +qux (int x) +{ + if (!x) + return 1; + thread_local int a; // { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } } + return ++a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } .-1 } +} + +constexpr int +garply (int x) +{ + if (!x) + return 1; + extern thread_local int a; // { dg-error "'a' declared 'thread_local' in 'constexpr' function only available with" "" { target c++20_down } } + return ++a; +} + +struct S { S (); ~S (); int s; }; // { dg-message "'S' is not literal because:" "" { target c++20_down } } + // { dg-message "'S' has a non-trivial destructor" "" { target c++17_down } .-1 } + // { dg-message "'S' does not have 'constexpr' destructor" "" { target { c++20_only } } .-2 } + +constexpr int +corge (int x) +{ + if (!x) + return 1; + S s; // { dg-error "variable 's' of non-literal type 'S' in 'constexpr' function only available with" "" { target c++20_down } } + return 0; +} + +#if __cpp_constexpr >= 202103L +static_assert (foo ()); +static_assert (bar (0)); +static_assert (baz (0)); +static_assert (qux (0)); +static_assert (garply (0)); +static_assert (corge (0)); +#endif --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C.jj 2021-10-05 12:54:29.910321212 +0200 +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit2.C 2021-10-05 12:54:29.910321212 +0200 @@ -0,0 +1,64 @@ +// P2242R3 +// { dg-do compile } +// { dg-options "-std=c++2b" } + +constexpr int +foo () +{ +lab: + return 1; +} + +constexpr int +bar (int x) +{ + if (x) + goto lab; // { dg-error "'goto' is not a constant expression" } + return 1; +lab: + return 0; +} + +constexpr int +baz (int x) +{ + if (!x) + return 1; + static int a; // { dg-error "control passes through declaration of 'a' with static storage duration" } + return ++a; +} + +constexpr int +qux (int x) +{ + if (!x) + return 1; + thread_local int a; // { dg-error "control passes through declaration of 'a' with thread storage duration" } + return ++a; +} + +constexpr int +garply (int x) +{ + if (!x) + return 1; + thread_local int a; // { dg-error "control passes through declaration of 'a' with thread storage duration" } + return ++a; +} + +struct S { S (); ~S (); int s; }; // { dg-message "'S::S\\\(\\\)' declared here" } + +constexpr int +corge (int x) +{ + if (!x) + return 1; + S s; // { dg-error "call to non-'constexpr' function 'S::S\\\(\\\)'" } + return 0; +} + +constexpr int a = bar (1); // { dg-message "in 'constexpr' expansion of" } +constexpr int b = baz (1); // { dg-message "in 'constexpr' expansion of" } +constexpr int c = qux (1); // { dg-message "in 'constexpr' expansion of" } +constexpr int d = garply (1); // { dg-message "in 'constexpr' expansion of" } +constexpr int e = corge (1); // { dg-message "in 'constexpr' expansion of" } --- gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C.jj 2021-10-05 12:54:29.910321212 +0200 +++ gcc/testsuite/g++.dg/cpp23/constexpr-nonlit3.C 2021-10-05 13:57:21.176734894 +0200 @@ -0,0 +1,10 @@ +// P2242R3 +// { dg-do compile { target c++14 } } + +constexpr int +foo () +{ + goto lab; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } +lab: // { dg-error "'goto' is not a constant expression" "" { target { c++23 } } .-1 } + return 1; +} --- gcc/testsuite/g++.dg/diagnostic/constexpr1.C.jj 2020-01-14 20:02:46.815609354 +0100 +++ gcc/testsuite/g++.dg/diagnostic/constexpr1.C 2021-10-05 13:56:30.249443508 +0200 @@ -1,5 +1,5 @@ // { dg-do compile { target c++11 } } -constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. declared .thread_local." } +constexpr int foo() { thread_local int i __attribute__((unused)) {}; return 1; } // { dg-error "40:.i. declared .thread_local." "" { target c++20_down } } -constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. declared .static." } +constexpr int bar() { static int i __attribute__((unused)) {}; return 1; } // { dg-error "34:.i. declared .static." "" { target c++20_down } } --- gcc/testsuite/g++.dg/cpp1y/constexpr-label.C.jj 2020-11-22 23:21:35.866434202 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-label.C 2021-10-05 13:37:37.660219970 +0200 @@ -4,6 +4,6 @@ constexpr int f () { -x: // { dg-error "label definition is not a constant expression" } +x: // { dg-error "label definition is not a constant expression" "" { target c++20_down } } return 42; } --- gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C.jj 2020-01-14 20:02:46.773609984 +0100 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-neg1.C 2021-10-05 13:38:23.322583787 +0200 @@ -3,13 +3,13 @@ struct A { A(); }; constexpr int f(int i) { - static int j = i; // { dg-error "static" } - thread_local int l = i; // { dg-error "thread_local" } + static int j = i; // { dg-error "static" "" { target c++20_down } } + thread_local int l = i; // { dg-error "thread_local" "" { target c++20_down } } goto foo; // { dg-error "goto" } foo: asm("foo"); // { dg-error "asm" "" { target c++17_down } } int k; // { dg-error "uninitialized" "" { target c++17_down } } - A a; // { dg-error "non-literal" } + A a; // { dg-error "non-literal" "" { target c++20_down } } return i; } --- gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C.jj 2020-01-14 20:02:46.000000000 +0100 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-try5.C 2021-10-05 14:05:21.092057240 +0200 @@ -5,14 +5,14 @@ constexpr int foo () try { // { dg-warning "function-try-block body of 'constexpr' function only available with" "" { target c++17_down } } int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } - static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function" } - goto l; // { dg-error "'goto' in 'constexpr' function" } - l:; + static double b = 1.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } + goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } + l:; // { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 } return 0; } catch (...) { long int c; // { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } } - static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function" } - goto l2; // { dg-error "'goto' in 'constexpr' function" } + static float d = 2.0f;// { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } + goto l2; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l2:; return -1; } @@ -20,20 +20,20 @@ try { // { dg-warning "function-try-bl constexpr int bar () { int a; // { dg-error "uninitialized variable 'a' in 'constexpr' function" "" { target c++17_down } } - static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function" } - goto l; // { dg-error "'goto' in 'constexpr' function" } - l:; + static long double b = 3.0;// { dg-error "'b' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } + goto l; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } + l:; // { dg-error "'goto' is not a constant expression" "" { target c++23 } .-1 } try { // { dg-warning "'try' in 'constexpr' function only available with" "" { target c++17_down } } short c; // { dg-error "uninitialized variable 'c' in 'constexpr' function" "" { target c++17_down } } - static float d; // { dg-error "'d' declared 'static' in 'constexpr' function" } + static float d; // { dg-error "'d' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } // { dg-error "uninitialized variable 'd' in 'constexpr' function" "" { target c++17_down } .-1 } - goto l2; // { dg-error "'goto' in 'constexpr' function" } + goto l2; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l2:; return 0; } catch (int) { char e; // { dg-error "uninitialized variable 'e' in 'constexpr' function" "" { target c++17_down } } - static int f = 5; // { dg-error "'f' declared 'static' in 'constexpr' function" } - goto l3; // { dg-error "'goto' in 'constexpr' function" } + static int f = 5; // { dg-error "'f' declared 'static' in 'constexpr' function only available with" "" { target c++20_down } } + goto l3; // { dg-error "'goto' in 'constexpr' function only available with" "" { target c++20_down } } l3:; return 1; } --- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C.jj 2021-04-14 19:19:14.049804260 +0200 +++ gcc/testsuite/g++.dg/cpp2a/constexpr-dtor3.C 2021-10-05 13:40:48.282564167 +0200 @@ -180,6 +180,6 @@ f7 () constexpr int f8 () { - T t4; // { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function" } + T t4; // { dg-error "variable 't4' of non-literal type 'T' in 'constexpr' function only available with" "" { target c++20_down } } return 0; } --- gcc/testsuite/g++.dg/cpp2a/consteval3.C.jj 2020-02-28 23:21:00.540567921 +0100 +++ gcc/testsuite/g++.dg/cpp2a/consteval3.C 2021-10-05 13:42:17.371322962 +0200 @@ -1,5 +1,4 @@ -// { dg-do compile } -// { dg-options "-std=c++2a" } +// { dg-do compile { target c++20 } } struct S { S () : a (0), b (1) {} int a, b; }; int f1 (); // { dg-message "previous declaration 'int f1\\(\\)'" } @@ -57,7 +56,7 @@ template consteval float f12 (float x); consteval int f13 (int x) { - static int a = 5; // { dg-error "'a' declared 'static' in 'consteval' function" } - thread_local int b = 6; // { dg-error "'b' declared 'thread_local' in 'consteval' function" } + static int a = 5; // { dg-error "'a' declared 'static' in 'consteval' function only available with" "" { target c++20_only } } + thread_local int b = 6; // { dg-error "'b' declared 'thread_local' in 'consteval' function only available with" "" { target c++20_only } } return x; }