Re: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
On 11/28/23 12:52, Jakub Jelinek wrote: On Tue, Nov 28, 2023 at 11:31:48AM -0500, Jason Merrill wrote: + if (len) + { + if (data) + msg = c_getstr (data); + if (msg == NULL) + buf = XNEWVEC (char, len); Jonathan pointed out elsewhere that this gets leaked if error return prevents us from getting to the XDELETEVEC. Seems it is just one of the returns, so ok to just XDELETEVEC there, or should I add some RAII for that? The other error return after this point is for !len case and so buf isn't allocated. RAII is generally preferable, but this is sufficient. 2023-11-28 Jakub Jelinek * semantics.cc (finish_static_assert): Free buf on error return. --- gcc/cp/semantics.cc.jj 2023-11-25 10:28:27.778191561 +0100 +++ gcc/cp/semantics.cc 2023-11-28 18:50:00.094733919 +0100 @@ -11582,6 +11582,7 @@ finish_static_assert (tree condition, tr error_at (location, "% message % " "must be a constant expression", i); + XDELETEVEC (buf); return; } if (msg == NULL) Jakub
Re: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
On Tue, Nov 28, 2023 at 11:31:48AM -0500, Jason Merrill wrote: > > + if (len) > > + { > > + if (data) > > + msg = c_getstr (data); > > + if (msg == NULL) > > + buf = XNEWVEC (char, len); > > Jonathan pointed out elsewhere that this gets leaked if error return > prevents us from getting to the XDELETEVEC. Seems it is just one of the returns, so ok to just XDELETEVEC there, or should I add some RAII for that? The other error return after this point is for !len case and so buf isn't allocated. 2023-11-28 Jakub Jelinek * semantics.cc (finish_static_assert): Free buf on error return. --- gcc/cp/semantics.cc.jj 2023-11-25 10:28:27.778191561 +0100 +++ gcc/cp/semantics.cc 2023-11-28 18:50:00.094733919 +0100 @@ -11582,6 +11582,7 @@ finish_static_assert (tree condition, tr error_at (location, "% message % " "must be a constant expression", i); + XDELETEVEC (buf); return; } if (msg == NULL) Jakub
Fix 'g++.dg/cpp26/static_assert1.C' for '-fno-exceptions' configurations (was: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348])
Hi! On 2023-11-23T09:32:24+0100, Jakub Jelinek wrote: > Here is what I've committed > --- gcc/testsuite/g++.dg/cpp26/static_assert1.C.jj2023-11-22 > 10:17:41.340064988 +0100 > +++ gcc/testsuite/g++.dg/cpp26/static_assert1.C 2023-11-22 > 10:47:45.045848504 +0100 > @@ -0,0 +1,309 @@ > +// C++26 P2741R3 - user-generated static_assert messages OK to push the attached "Fix 'g++.dg/cpp26/static_assert1.C' for '-fno-exceptions' configurations"? Grüße Thomas > +// { dg-do compile { target c++11 } } > +// { dg-options "" } > + > +static_assert (true, ""); > +static_assert (true, ("")); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'static_assert' message must be > a string literal or object with 'size' and 'data' members" "" { target *-*-* > } .-1 } > + // { dg-error "request for member 'size' in > '\\\(\\\"\\\"\\\)', which is of non-class type 'const char \\\[1\\\]'" "" { > target *-*-* } .-2 } > +static_assert (true, "" + 0);// { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'static_assert' message must be > a string literal or object with 'size' and 'data' members" "" { target *-*-* > } .-1 } > + // { dg-error "request for member 'size' in > '\\\(const char\\\*\\\)\\\"\\\"', which is of non-class type 'const > char\\\*'" "" { target *-*-* } .-2 } > +static_assert (true, 0); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'static_assert' message must be > a string literal or object with 'size' and 'data' members" "" { target *-*-* > } .-1 } > + // { dg-error "request for member 'size' in > '0', which is of non-class type 'int'" "" { target *-*-* } .-2 } > +struct A {}; > +static_assert (true, A {}); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'static_assert' message must be > a string literal or object with 'size' and 'data' members" "" { target *-*-* > } .-1 } > + // { dg-error "'struct A' has no member named > 'size'" "" { target *-*-* } .-2 } > +struct B { int size; }; > +static_assert (true, B {}); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'static_assert' message must be > a string literal or object with 'size' and 'data' members" "" { target *-*-* > } .-1 } > + // { dg-error "'struct B' has no member named > 'data'" "" { target *-*-* } .-2 } > +struct C { constexpr int size () const { return 0; } }; > +static_assert (true, C {}); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'static_assert' message must be > a string literal or object with 'size' and 'data' members" "" { target *-*-* > } .-1 } > + // { dg-error "'struct C' has no member named > 'data'" "" { target *-*-* } .-2 } > +struct D { constexpr int size () const { return 0; } int data; }; > +static_assert (true, D {}); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'D\\\(\\\).D::data' cannot be > used as a function" "" { target *-*-* } .-1 } > +struct E { int size = 0; > +constexpr const char *data () const { return ""; } }; > +static_assert (true, E {}); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'E\\\(\\\).E::size' cannot be > used as a function" "" { target c++11_only } .-1 } > + // { dg-error "'E\\\{0\\\}.E::size' cannot be > used as a function" "" { target c++14 } .-2 } > +struct F { constexpr const char *size () const { return ""; } > +constexpr const char *data () const { return ""; } }; > +static_assert (true, F {}); // { dg-warning "'static_assert' with > non-string message only available with" "" { target c++23_down } } > + // { dg-error "'static_assert' message > 'size\\\(\\\)' must be implicitly convertible to 'std::size_t'" "" { target > *-*-* } .-1 } > + // { dg-error "could not convert > 'F\\\(\\\).F::size\\\(\\\)' from 'const char\\\*' to '\[^']*'" "" { target > *-*-* } .-2 } > + // { dg-error "conversion from 'const char\\\*' > to '\[^']*' in a converted constant expression" "" { target *-*-* } .-3 } > +struct G { conste
Re: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
On 11/23/23 03:32, Jakub Jelinek wrote: On Wed, Nov 22, 2023 at 04:53:48PM -0500, Jason Merrill wrote: I agree it's weird to get two of the same error, but maybe instead of duplicating the error, we could look up data only if size succeeded, and then error once if either failed? Here is what I've committed after another bootstrap/regtest on x86_64-linux and i686-linux. Besides the above requested change I've tweaked 2 lines in the test not to rely on a particular std::size_t exact type because otherwise the test failed on i686-linux. And accepting there only the current unsigned int long unsigned int long long unsinged int unsigned __int20__ (or how exactly is this one spelled in diagnostics) seems fragile. --- gcc/cp/semantics.cc.jj 2023-11-22 11:30:08.019325101 +0100 +++ gcc/cp/semantics.cc 2023-11-22 22:58:25.194480633 +0100 @@ -11485,9 +11534,89 @@ finish_static_assert (tree condition, tr if (processing_template_decl) goto defer; - int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT -(TREE_TYPE (TREE_TYPE (message; - int len = TREE_STRING_LENGTH (message) / sz - 1; + int len; + const char *msg = NULL; + char *buf = NULL; + if (message_sz && message_data) + { + tree msz = cxx_constant_value (message_sz, NULL_TREE, complain); + if (!tree_fits_uhwi_p (msz)) + { + error_at (location, + "% message % " + "must be a constant expression"); + return; + } + else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz) + != tree_to_uhwi (msz)) + { + error_at (location, + "% message % " + "%qE too large", msz); + return; + } + len = tree_to_uhwi (msz); + tree data = maybe_constant_value (message_data, NULL_TREE, + mce_true); + if (!reduced_constant_expression_p (data)) + data = NULL_TREE; + if (len) + { + if (data) + msg = c_getstr (data); + if (msg == NULL) + buf = XNEWVEC (char, len); Jonathan pointed out elsewhere that this gets leaked if error return prevents us from getting to the XDELETEVEC. + for (int i = 0; i < len; ++i) + { + tree t = message_data; + if (i) + t = build2 (POINTER_PLUS_EXPR, + TREE_TYPE (message_data), message_data, + size_int (i)); + t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t); + tree t2 = cxx_constant_value (t, NULL_TREE, complain); + if (!tree_fits_shwi_p (t2)) + { + error_at (location, + "% message % " + "must be a constant expression", i); + return; + } + if (msg == NULL) + buf[i] = tree_to_shwi (t2); + /* If c_getstr worked, just verify the first and +last characters using constant evaluation. */ + else if (len > 2 && i == 0) + i = len - 2; + } + if (msg == NULL) + msg = buf; + } + else if (!data) + { + /* We don't have any function to test whether some +expression is a core constant expression. So, instead +test whether (message.data (), 0) is a constant +expression. */ + data = build2 (COMPOUND_EXPR, integer_type_node, +message_data, integer_zero_node); + tree t = cxx_constant_value (data, NULL_TREE, complain); + if (!integer_zerop (t)) + { + error_at (location, + "% message % " + "must be a core constant expression"); + return; + } + } + } + else + { + tree eltype = TREE_TYPE (TREE_TYPE (message)); + int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (eltype)); + msg = TREE_STRING_POINTER (message); + len = TREE_STRING_LENGTH (message) / sz - 1; + } /* See if we can find which clause was failing (for logical AND). */ tree bad = find_failing_clause (NULL, orig_condition); @@ -11497,12 +11626,13 @@
Re: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
On Wed, Nov 22, 2023 at 04:53:48PM -0500, Jason Merrill wrote: > I agree it's weird to get two of the same error, but maybe instead of > duplicating the error, we could look up data only if size succeeded, and > then error once if either failed? Here is what I've committed after another bootstrap/regtest on x86_64-linux and i686-linux. Besides the above requested change I've tweaked 2 lines in the test not to rely on a particular std::size_t exact type because otherwise the test failed on i686-linux. And accepting there only the current unsigned int long unsigned int long long unsinged int unsigned __int20__ (or how exactly is this one spelled in diagnostics) seems fragile. Thanks a lot for the review of this (and sorry it took so long on my side because I've missed the first review). 2023-11-23 Jakub Jelinek PR c++/110348 gcc/ * doc/invoke.texi (-Wno-c++26-extensions): Document. gcc/c-family/ * c.opt (Wc++26-extensions): New option. * c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine __cpp_static_assert to 202306L rather than 201411L. gcc/cp/ * parser.cc: Implement C++26 P2741R3 - user-generated static_assert messages. (cp_parser_static_assert): Parse message argument as conditional-expression if it is not a pure string literal or several of them concatenated followed by closing paren. * semantics.cc (finish_static_assert): Handle message which is not STRING_CST. For condition with bare parameter packs return early. * pt.cc (tsubst_expr) : Also tsubst_expr message and make sure that if it wasn't originally STRING_CST, it isn't after tsubst_expr either. gcc/testsuite/ * g++.dg/cpp26/static_assert1.C: New test. * g++.dg/cpp26/feat-cxx26.C (__cpp_static_assert): Expect 202306L rather than 201411L. * g++.dg/cpp0x/udlit-error1.C: Expect different diagnostics for static_assert with user-defined literal. --- gcc/doc/invoke.texi.jj 2023-11-22 10:14:56.021376360 +0100 +++ gcc/doc/invoke.texi 2023-11-22 10:17:41.328065157 +0100 @@ -9107,6 +9107,13 @@ Do not warn about C++23 constructs in co an older C++ standard. Even without this option, some C++23 constructs will only be diagnosed if @option{-Wpedantic} is used. +@opindex Wc++26-extensions +@opindex Wno-c++26-extensions +@item -Wno-c++26-extensions @r{(C++ and Objective-C++ only)} +Do not warn about C++26 constructs in code being compiled using +an older C++ standard. Even without this option, some C++26 constructs +will only be diagnosed if @option{-Wpedantic} is used. + @opindex Wcast-qual @opindex Wno-cast-qual @item -Wcast-qual --- gcc/c-family/c.opt.jj 2023-11-22 10:14:55.963377171 +0100 +++ gcc/c-family/c.opt 2023-11-22 10:17:41.328065157 +0100 @@ -498,6 +498,10 @@ Wc++23-extensions C++ ObjC++ Var(warn_cxx23_extensions) Warning Init(1) Warn about C++23 constructs in code compiled with an older standard. +Wc++26-extensions +C++ ObjC++ Var(warn_cxx26_extensions) Warning Init(1) +Warn about C++26 constructs in code compiled with an older standard. + Wcast-function-type C ObjC C++ ObjC++ Var(warn_cast_function_type) Warning EnabledBy(Wextra) Warn about casts between incompatible function types. --- gcc/c-family/c-cppbuiltin.cc.jj 2023-11-22 10:14:55.962377185 +0100 +++ gcc/c-family/c-cppbuiltin.cc2023-11-22 10:17:41.329065143 +0100 @@ -1023,7 +1023,8 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++17. */ cpp_define (pfile, "__cpp_unicode_characters=201411L"); - cpp_define (pfile, "__cpp_static_assert=201411L"); + if (cxx_dialect <= cxx23) + cpp_define (pfile, "__cpp_static_assert=201411L"); cpp_define (pfile, "__cpp_namespace_attributes=201411L"); cpp_define (pfile, "__cpp_enumerator_attributes=201411L"); cpp_define (pfile, "__cpp_nested_namespace_definitions=201411L"); @@ -1086,6 +1087,7 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++26. */ cpp_define (pfile, "__cpp_constexpr=202306L"); + cpp_define (pfile, "__cpp_static_assert=202306L"); } if (flag_concepts) { --- gcc/cp/parser.cc.jj 2023-11-22 10:14:55.969377087 +0100 +++ gcc/cp/parser.cc2023-11-22 10:17:41.335065058 +0100 @@ -16616,6 +16616,7 @@ cp_parser_linkage_specification (cp_pars static_assert-declaration: static_assert ( constant-expression , string-literal ) ; static_assert ( constant-expression ) ; (C++17) + static_assert ( constant-expression, conditional-expression ) ; (C++26) If MEMBER_P, this static_assert is a class member. */ @@ -16646,10 +16647,10 @@ cp_parser_static_assert (cp_parser *pars /* Parse the constant-expression. Allow a non-constant expression here in order to give better diagnostics in finish_static_assert. */ - condition = -
Re: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
On 11/22/23 05:00, Jakub Jelinek wrote: On Tue, Nov 21, 2023 at 10:51:36PM -0500, Jason Merrill wrote: Actually, let's go back to the previous message, but change the tf_nones above to 'complain' so that we see those errors and then this explanation. Likewise with the conversion checks later in the function. So like this? Besides what you asked for I've separated the diagnostics for when size member isn't found in lookup vs. when data isn't found, because it looked weird to get 2 same errors e.g. in the udlit-error1.C case. + message_sz + = finish_class_member_access_expr (message, + get_identifier ("size"), + false, complain); + if (message_sz == error_mark_node) + { + error_at (location, "% message must be a string " + "literal or object with % and " + "% members"); + return; + } + message_data + = finish_class_member_access_expr (message, + get_identifier ("data"), + false, complain); + if (message_data == error_mark_node) + { + error_at (location, "% message must be a string " + "literal or object with % and " + "% members"); + return; + } I agree it's weird to get two of the same error, but maybe instead of duplicating the error, we could look up data only if size succeeded, and then error once if either failed? OK with that change. Jason
[PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
On Tue, Nov 21, 2023 at 10:51:36PM -0500, Jason Merrill wrote: > Actually, let's go back to the previous message, but change the tf_nones > above to 'complain' so that we see those errors and then this explanation. > Likewise with the conversion checks later in the function. So like this? Besides what you asked for I've separated the diagnostics for when size member isn't found in lookup vs. when data isn't found, because it looked weird to get 2 same errors e.g. in the udlit-error1.C case. So far tested with GXX_TESTSUITE_STDS=98,11,14,17,20,23,26 make check-g++ RUNTESTFLAGS="dg.exp='static_assert1.C feat-cxx26.C udlit-error1.C'" Ok if it passes full bootstrap/regtest? 2023-11-22 Jakub Jelinek PR c++/110348 gcc/ * doc/invoke.texi (-Wno-c++26-extensions): Document. gcc/c-family/ * c.opt (Wc++26-extensions): New option. * c-cppbuiltin.cc (c_cpp_builtins): For C++26 predefine __cpp_static_assert to 202306L rather than 201411L. gcc/cp/ * parser.cc: Implement C++26 P2741R3 - user-generated static_assert messages. (cp_parser_static_assert): Parse message argument as conditional-expression if it is not a pure string literal or several of them concatenated followed by closing paren. * semantics.cc (finish_static_assert): Handle message which is not STRING_CST. For condition with bare parameter packs return early. * pt.cc (tsubst_expr) : Also tsubst_expr message and make sure that if it wasn't originally STRING_CST, it isn't after tsubst_expr either. gcc/testsuite/ * g++.dg/cpp26/static_assert1.C: New test. * g++.dg/cpp26/feat-cxx26.C (__cpp_static_assert): Expect 202306L rather than 201411L. * g++.dg/cpp0x/udlit-error1.C: Expect different diagnostics for static_assert with user-defined literal. --- gcc/doc/invoke.texi.jj 2023-11-22 10:14:56.021376360 +0100 +++ gcc/doc/invoke.texi 2023-11-22 10:17:41.328065157 +0100 @@ -9107,6 +9107,13 @@ Do not warn about C++23 constructs in co an older C++ standard. Even without this option, some C++23 constructs will only be diagnosed if @option{-Wpedantic} is used. +@opindex Wc++26-extensions +@opindex Wno-c++26-extensions +@item -Wno-c++26-extensions @r{(C++ and Objective-C++ only)} +Do not warn about C++26 constructs in code being compiled using +an older C++ standard. Even without this option, some C++26 constructs +will only be diagnosed if @option{-Wpedantic} is used. + @opindex Wcast-qual @opindex Wno-cast-qual @item -Wcast-qual --- gcc/c-family/c.opt.jj 2023-11-22 10:14:55.963377171 +0100 +++ gcc/c-family/c.opt 2023-11-22 10:17:41.328065157 +0100 @@ -498,6 +498,10 @@ Wc++23-extensions C++ ObjC++ Var(warn_cxx23_extensions) Warning Init(1) Warn about C++23 constructs in code compiled with an older standard. +Wc++26-extensions +C++ ObjC++ Var(warn_cxx26_extensions) Warning Init(1) +Warn about C++26 constructs in code compiled with an older standard. + Wcast-function-type C ObjC C++ ObjC++ Var(warn_cast_function_type) Warning EnabledBy(Wextra) Warn about casts between incompatible function types. --- gcc/c-family/c-cppbuiltin.cc.jj 2023-11-22 10:14:55.962377185 +0100 +++ gcc/c-family/c-cppbuiltin.cc2023-11-22 10:17:41.329065143 +0100 @@ -1023,7 +1023,8 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++17. */ cpp_define (pfile, "__cpp_unicode_characters=201411L"); - cpp_define (pfile, "__cpp_static_assert=201411L"); + if (cxx_dialect <= cxx23) + cpp_define (pfile, "__cpp_static_assert=201411L"); cpp_define (pfile, "__cpp_namespace_attributes=201411L"); cpp_define (pfile, "__cpp_enumerator_attributes=201411L"); cpp_define (pfile, "__cpp_nested_namespace_definitions=201411L"); @@ -1086,6 +1087,7 @@ c_cpp_builtins (cpp_reader *pfile) { /* Set feature test macros for C++26. */ cpp_define (pfile, "__cpp_constexpr=202306L"); + cpp_define (pfile, "__cpp_static_assert=202306L"); } if (flag_concepts) { --- gcc/cp/parser.cc.jj 2023-11-22 10:14:55.969377087 +0100 +++ gcc/cp/parser.cc2023-11-22 10:17:41.335065058 +0100 @@ -16616,6 +16616,7 @@ cp_parser_linkage_specification (cp_pars static_assert-declaration: static_assert ( constant-expression , string-literal ) ; static_assert ( constant-expression ) ; (C++17) + static_assert ( constant-expression, conditional-expression ) ; (C++26) If MEMBER_P, this static_assert is a class member. */ @@ -16646,10 +16647,10 @@ cp_parser_static_assert (cp_parser *pars /* Parse the constant-expression. Allow a non-constant expression here in order to give better diagnostics in finish_static_assert. */ - condition = -cp_parser_constant_expression (parser, - /*allow_non_constant_p=*/true, -