]> git.ipfire.org Git - thirdparty/gcc.git/commit
c++: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
authorJakub Jelinek <jakub@redhat.com>
Thu, 23 Nov 2023 08:13:37 +0000 (09:13 +0100)
committerJakub Jelinek <jakub@redhat.com>
Thu, 23 Nov 2023 08:13:37 +0000 (09:13 +0100)
commit6ce952188ab39e303e4f63e474b5cba83b5b12fd
treeca45fd1f5973a072feeceddeed94b9d23ff09159
parent9d912820d02c7396676e04c4c05f6a0fdd92ed85
c++: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]

The following patch implements the user generated static_assert messages next
to string literals.

As I wrote already in the PR, in addition to looking through the paper
I looked at the clang++ testcase for this feature implemented there from
paper's author and on godbolt played with various parts of the testcase
coverage below, and there are some differences between what the patch
implements and what clang++ implements.

The first is that clang++ diagnoses if M.size () or M.data () methods
are present, but aren't constexpr; while the paper introduction talks about
that, the standard wording changes don't seem to require that, all they say
is that those methods need to exist (assuming accessible and the like)
and be implicitly convertible to std::size_t or const char *, but rest is
only if the static assertion fails.  If there is intent to change that
wording, the question is how far to go, e.g. while M.size () could be
constexpr, they could e.g. return some class object which wouldn't have
constexpr conversion operator to size_t/const char * and tons of other
reasons why the constant evaluation could fail.  Without actually evaluating
it I don't see how we could guarantee anything for non-failed static_assert.

The second difference is that
static_assert (false, "foo"_myd);
in the testcase is normal failed static assertion and
static_assert (true, "foo"_myd);
would be accepted, while clang++ rejects it.  IMHO
"foo"_myd doesn't match the syntactic requirements of unevaluated-string
as mentioned in http://eel.is/c++draft/dcl.pre#10 , and because
a constexpr udlit operator can return something which is valid, it shouldn't
be rejected just in case.
Last is clang++ ICEs on non-static data members size/data.

The first version of this support had a difference where M.data () was not
a constant expression but a core constant expression, but if M.size () != 0
M.data ()[0] ... M.data ()[M.size () - 1] were integer constant expressions.
We don't have any routine to test whether an expression is a core constant
expression, so what the code does is try silently whether M.data () is
a constant expression (maybe_constant_value), if it is, nice, we can use
that result to attempt to optimize the extraction of the message from it
if it is some recognized form involving a STRING_CST and just to double-check
try to constant evaluate M.data ()[0] and M.data ()[M.size () - 1] expressions
as boundaries but not anything in between.  If M.data () is not a constant
expression, we don't fail, but use a slower method of evaluating M.data ()[i]
for i 0, 1, ... M.size () - 1.  And if M.size () == 0, the above wouldn't
evaluate anything, so we try to constant evaluate (M.data (), 0) as constant
expression, which should succeed if M.data () is a core constant expression
and fail otherwise.

The patch assumes that these expressions are manifestly constant evaluated.

The patch implements what I see in the paper, because it is unclear what
further changes will be voted in (and the changes can be done at that
point).
The initial patch used tf_none in 6 spots so that just the static_assert
specific errors were emitted and not others, but during review this has been
changed, so that we emit both the more detailed errors why something wasn't
found or wasn't callable or wasn't convertible and diagnostics that
static_assert second argument needs to satisfy some of the needed properties.

2023-11-23  Jakub Jelinek  <jakub@redhat.com>

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) <case STATIC_ASSERT>: 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/c-family/c-cppbuiltin.cc
gcc/c-family/c.opt
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/cp/semantics.cc
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/cpp0x/udlit-error1.C
gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
gcc/testsuite/g++.dg/cpp26/static_assert1.C [new file with mode: 0644]