From: Jakub Jelinek <jakub@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++, v4: Implement C++26 P2741R3 - user-generated static_assert messages [PR110348]
Date: Thu, 23 Nov 2023 09:32:24 +0100 [thread overview]
Message-ID: <ZV8OGMkGJFMG5urh@tucnak> (raw)
In-Reply-To: <b8972bb7-42b9-4a7d-866a-8cc17dabe131@redhat.com>
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 <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/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.cc 2023-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.cc 2023-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,
- /*non_constant_p=*/nullptr);
+ condition
+ = cp_parser_constant_expression (parser,
+ /*allow_non_constant_p=*/true,
+ /*non_constant_p=*/nullptr);
if (cp_lexer_peek_token (parser->lexer)->type == CPP_CLOSE_PAREN)
{
@@ -16668,8 +16669,32 @@ cp_parser_static_assert (cp_parser *pars
/* Parse the separating `,'. */
cp_parser_require (parser, CPP_COMMA, RT_COMMA);
- /* Parse the string-literal message. */
- if (cxx_dialect >= cxx26)
+ /* Parse the message expression. */
+ bool string_lit = true;
+ for (unsigned int i = 1; ; ++i)
+ {
+ cp_token *tok = cp_lexer_peek_nth_token (parser->lexer, i);
+ if (cp_parser_is_pure_string_literal (tok))
+ continue;
+ else if (tok->type == CPP_CLOSE_PAREN)
+ break;
+ string_lit = false;
+ break;
+ }
+ if (!string_lit)
+ {
+ location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+ if (cxx_dialect < cxx26)
+ pedwarn (loc, OPT_Wc__26_extensions,
+ "%<static_assert%> with non-string message only "
+ "available with %<-std=c++2c%> or %<-std=gnu++2c%>");
+
+ message = cp_parser_conditional_expression (parser);
+ if (TREE_CODE (message) == STRING_CST)
+ message = build1_loc (loc, PAREN_EXPR, TREE_TYPE (message),
+ message);
+ }
+ else if (cxx_dialect >= cxx26)
message = cp_parser_unevaluated_string_literal (parser);
else
message = cp_parser_string_literal (parser, /*translate=*/false,
--- 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
@@ -11434,6 +11434,7 @@ finish_static_assert (tree condition, tr
bool member_p, bool show_expr_p)
{
tsubst_flags_t complain = tf_warning_or_error;
+ tree message_sz = NULL_TREE, message_data = NULL_TREE;
if (message == NULL_TREE
|| message == error_mark_node
@@ -11441,13 +11442,61 @@ finish_static_assert (tree condition, tr
|| condition == error_mark_node)
return;
- if (check_for_bare_parameter_packs (condition))
- condition = error_mark_node;
+ if (check_for_bare_parameter_packs (condition)
+ || check_for_bare_parameter_packs (message))
+ return;
+
+ if (TREE_CODE (message) != STRING_CST
+ && !type_dependent_expression_p (message))
+ {
+ message_sz
+ = finish_class_member_access_expr (message,
+ get_identifier ("size"),
+ false, complain);
+ if (message_sz != error_mark_node)
+ message_data
+ = finish_class_member_access_expr (message,
+ get_identifier ("data"),
+ false, complain);
+ if (message_sz == error_mark_node || message_data == error_mark_node)
+ {
+ error_at (location, "%<static_assert%> message must be a string "
+ "literal or object with %<size%> and "
+ "%<data%> members");
+ return;
+ }
+ releasing_vec size_args, data_args;
+ message_sz = finish_call_expr (message_sz, &size_args, false, false,
+ complain);
+ message_data = finish_call_expr (message_data, &data_args, false, false,
+ complain);
+ if (message_sz == error_mark_node || message_data == error_mark_node)
+ return;
+ message_sz = build_converted_constant_expr (size_type_node, message_sz,
+ complain);
+ if (message_sz == error_mark_node)
+ {
+ error_at (location, "%<static_assert%> message %<size()%> "
+ "must be implicitly convertible to "
+ "%<std::size_t%>");
+ return;
+ }
+ message_data = build_converted_constant_expr (const_string_type_node,
+ message_data, complain);
+ if (message_data == error_mark_node)
+ {
+ error_at (location, "%<static_assert%> message %<data()%> "
+ "must be implicitly convertible to "
+ "%<const char*%>");
+ return;
+ }
+ }
/* Save the condition in case it was a concept check. */
tree orig_condition = condition;
- if (instantiation_dependent_expression_p (condition))
+ if (instantiation_dependent_expression_p (condition)
+ || instantiation_dependent_expression_p (message))
{
/* We're in a template; build a STATIC_ASSERT and put it in
the right place. */
@@ -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,
+ "%<static_assert%> message %<size()%> "
+ "must be a constant expression");
+ return;
+ }
+ else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz)
+ != tree_to_uhwi (msz))
+ {
+ error_at (location,
+ "%<static_assert%> message %<size()%> "
+ "%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);
+ 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,
+ "%<static_assert%> message %<data()[%d]%> "
+ "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,
+ "%<static_assert%> message %<data()%> "
+ "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 @@ finish_static_assert (tree condition, tr
auto_diagnostic_group d;
- /* Report the error. */
+ /* Report the error. */
if (len == 0)
error_at (cloc, "static assertion failed");
else
- error_at (cloc, "static assertion failed: %s",
- TREE_STRING_POINTER (message));
+ error_at (cloc, "static assertion failed: %.*s", len, msg);
+
+ XDELETEVEC (buf);
diagnose_failing_condition (bad, cloc, show_expr_p);
}
--- gcc/cp/pt.cc.jj 2023-11-22 10:14:55.992376766 +0100
+++ gcc/cp/pt.cc 2023-11-22 10:17:41.340064988 +0100
@@ -18701,15 +18701,20 @@ tsubst_stmt (tree t, tree args, tsubst_f
case STATIC_ASSERT:
{
- tree condition;
+ tree condition, message;
++c_inhibit_evaluation_warnings;
condition = tsubst_expr (STATIC_ASSERT_CONDITION (t), args,
complain, in_decl);
+ message = tsubst_expr (STATIC_ASSERT_MESSAGE (t), args,
+ complain, in_decl);
+ if (TREE_CODE (STATIC_ASSERT_MESSAGE (t)) != STRING_CST
+ && TREE_CODE (message) == STRING_CST)
+ message = build1_loc (STATIC_ASSERT_SOURCE_LOCATION (t),
+ PAREN_EXPR, TREE_TYPE (message), message);
--c_inhibit_evaluation_warnings;
- finish_static_assert (condition,
- STATIC_ASSERT_MESSAGE (t),
+ finish_static_assert (condition, message,
STATIC_ASSERT_SOURCE_LOCATION (t),
/*member_p=*/false, /*show_expr_p=*/true);
}
--- gcc/testsuite/g++.dg/cpp26/static_assert1.C.jj 2023-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
+// { 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 { constexpr long size () const { return 0; }
+ constexpr float data () const { return 0.0f; } };
+static_assert (true, G {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "'static_assert' message 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 }
+ // { dg-error "could not convert 'G\\\(\\\).G::data\\\(\\\)' from 'float' to 'const char\\\*'" "" { target *-*-* } .-2 }
+struct H { short size () const { return 0; }
+ constexpr const char *data () const { return ""; } };
+static_assert (true, H {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+struct I { constexpr signed char size () const { return 0; }
+ const char *data () const { return ""; } };
+static_assert (true, I {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+struct J { constexpr int size () const { return j ? throw 1 : 0; } // { dg-error "expression '<throw-expression>' is not a constant expression" }
+ constexpr const char *data () const { return ""; };
+ constexpr J (int x) : j (x) {}
+ int j; };
+static_assert (true, J (1)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+static_assert (false, J (0)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed" "" { target *-*-* } .-1 }
+static_assert (false, J (1)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "'static_assert' message 'size\\\(\\\)' must be a constant expression" "" { target *-*-* } .-1 }
+struct K { constexpr operator int () { return 4; } };
+struct L { constexpr operator const char * () { return "test"; } };
+struct M { constexpr K size () const { return {}; }
+ constexpr L data () const { return {}; } };
+static_assert (true, M {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+static_assert (false, M {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+#if __cpp_constexpr_dynamic_alloc >= 201907L
+struct N { constexpr int size () const { return 3; }
+ constexpr const char *data () const { return new char[3] { 'b', 'a', 'd' }; } }; // { dg-error "'\\\* N\\\(\\\).N::data\\\(\\\)' is not a constant expression because allocated storage has not been deallocated" "" { target c++20 } }
+static_assert (true, N {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++20 && c++23_down } } }
+static_assert (false, N {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++20 && c++23_down } } }
+ // { dg-error "'static_assert' message 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++20 } .-1 }
+#endif
+constexpr const char a[] = { 't', 'e', 's', 't' };
+struct O { constexpr int size () const { return 4; }
+ constexpr const char *data () const { return a; } };
+static_assert (false, O {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+struct P { constexpr int size () const { return 4 - p; }
+ constexpr const char *data () const { return &a[p]; }
+ constexpr P (int x) : p (x) {}
+ int p; };
+static_assert (false, P (0)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+static_assert (false, P (2)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: st" "" { target *-*-* } .-1 }
+struct Q { constexpr int size () const { return 4 - q; }
+ constexpr const char *data () const { return &"test"[q]; }
+ constexpr Q (int x) : q (x) {}
+ int q; };
+static_assert (false, Q (0)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+static_assert (false, Q (1)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: est" "" { target *-*-* } .-1 }
+struct R { constexpr int size () const { return 4 - r; }
+ constexpr const char *d () const { return "test"; }
+ constexpr const char *data () const { return d () + r; }
+ constexpr R (int x) : r (x) {}
+ int r; };
+static_assert (false, R (0)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+static_assert (false, R (2)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: st" "" { target *-*-* } .-1 }
+struct S { constexpr float size (float) const { return 42.0f; }
+ constexpr int size (void * = nullptr) const { return 4; }
+ constexpr double data (double) const { return 42.0; }
+ constexpr const char *data (int = 0) const { return "test"; } };
+static_assert (true, S {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+static_assert (false, S {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+
+using size_t = decltype (sizeof (0));
+struct string_view {
+ size_t s;
+ const char *d;
+ constexpr string_view () : s (0), d (nullptr) {}
+ constexpr string_view (const char *p) : s (__builtin_strlen (p)), d (p) {}
+ constexpr string_view (size_t l, const char *p) : s (l), d (p) {}
+ constexpr size_t size () const noexcept { return s; }
+ constexpr const char *data () const noexcept { return d; }
+};
+static_assert (true, string_view{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+static_assert (false, string_view ("test")); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+static_assert (false, string_view ("א")); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: א" "" { target *-*-* } .-1 }
+static_assert (false, string_view (0, nullptr)); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed" "" { target *-*-* } .-1 }
+static_assert (false, string_view (4, "testwithextrachars")); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+static_assert (false, string_view (42, "test")); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "array subscript value '41' is outside the bounds of array type 'const char \\\[5\\\]'" "" { target *-*-* } .-1 }
+ // { dg-error "'static_assert' message 'data\\\(\\\)\\\[41\\\]' must be a constant expression" "" { target *-*-* } .-2 }
+
+template <typename T, size_t N>
+struct array {
+ constexpr size_t size () const { return N; }
+ constexpr const T *data () const { return a; }
+ const T a[N];
+};
+static_assert (true, array<char, 2> { 'O', 'K' }); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+static_assert (true, array<wchar_t, 2> { L'O', L'K' }); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "'static_assert' message 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 }
+ // { dg-error "could not convert 'array<wchar_t, 2>{const wchar_t \\\[2\\\]{\[0-9]+, \[0-9]+}}.array<wchar_t, 2>::data\\\(\\\)' from 'const wchar_t\\\*' to 'const char\\\*'" "" { target *-*-* } .-2 }
+static_assert (false, array<char, 4> { 't', 'e', 's', 't' }); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+
+void
+foo ()
+{
+ constexpr auto a = array<char, 4> { 't', 'e', 's', 't' };
+ static_assert (false, a); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+} // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+
+#if __cpp_constexpr_dynamic_alloc >= 201907L
+struct T {
+ const char *d = init ();
+ constexpr int size () const { return 4; }
+ constexpr const char *data () const { return d; }
+ constexpr const char *init () const { return new char[4] { 't', 'e', 's', 't' }; }
+ constexpr ~T () { delete[] d; }
+};
+static_assert (false, T{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++20 && c++23_down } } }
+ // { dg-error "static assertion failed: test" "" { target c++20 } .-1 }
+#endif
+struct U { constexpr operator const char * () const { return u; }
+ char u[5] = "test"; };
+#if __cplusplus >= 201402L
+struct V { constexpr auto size () const { return K{}; }
+ constexpr auto data () const { return U{}; } };
+static_assert (false, V{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
+ // { dg-error "static assertion failed: test" "" { target c++14 } .-1 }
+#endif
+struct W { constexpr int size (int) const { return 4; }
+ constexpr const char *data () const { return "test"; } };
+static_assert (true, W{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "no matching function for call to 'W::size\\\(\\\)'" "" { target *-*-* } .-1 }
+struct X { constexpr int size () const { return 4; }
+ constexpr const char *data (int) const { return "test"; } };
+static_assert (true, X{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "no matching function for call to 'X::data\\\(\\\)'" "" { target *-*-* } .-1 }
+struct Y { constexpr int size () { return 4; }
+ constexpr const char *data (int) { return "test"; } };
+static_assert (true, Y{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "no matching function for call to 'Y::data\\\(\\\)'" "" { target *-*-* } .-1 }
+#if __cpp_concepts >= 201907L
+struct Z { constexpr int size (auto...) const { return 4; }
+ constexpr const char *data (auto...) const { return "test"; } };
+static_assert (false, Z{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++20 && c++23_down } } }
+ // { dg-error "static assertion failed: test" "" { target c++20 } .-1 }
+#endif
+
+namespace NN
+{
+ template <typename T>
+ struct A {
+ constexpr int size () const = delete;
+ constexpr const char *data () const { return "test"; } };
+ static_assert (true, A<int>{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "use of deleted function 'constexpr int NN::A<T>::size\\\(\\\) const \\\[with T = int\\\]'" "" { target *-*-* } .-1 }
+#if __cpp_concepts >= 201907L
+ template <typename T>
+ struct B {
+ constexpr int size () const { return 4; }
+ constexpr const char *data () const requires false { return "test"; } };
+ static_assert (true, B<short>{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++20 && c++23_down } } }
+ // { dg-error "no matching function for call to 'NN::B<short int>::data\\\(\\\)'" "" { target c++20 } .-1 }
+#endif
+ class C {
+ constexpr int size () const = delete;
+ constexpr const char *data () const { return "test"; } };
+ static_assert (true, C{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "use of deleted function 'constexpr int NN::C::size\\\(\\\) const'" "" { target *-*-* } .-1 }
+ // { dg-error "'constexpr const char\\\* NN::C::data\\\(\\\) const' is private within this context" "" { target *-*-* } .-2 }
+#if __cplusplus >= 201402L
+ struct D {
+ constexpr int size () { return 4; }
+ constexpr int size () const { return 3; }
+ constexpr const char *data () { return "test"; }
+ constexpr const char *data () const { return "ehlo"; } };
+ static_assert (true, D{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
+ static_assert (false, D{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
+ // { dg-error "static assertion failed: test" "" { target c++14 } .-1 }
+#endif
+ struct E {
+ constexpr int size () const { return 4; }
+ constexpr const char *data () const { return "test"; } };
+ template <typename T>
+ struct F {
+ static_assert (false, T{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ }; // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+ template <typename T>
+ struct G {
+ static_assert (false, T{}); // { 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 'long int'" "" { target *-*-* } .-2 }
+ F<E> fe;
+ G<long> gl;
+ constexpr E operator ""_myd (const char *, size_t) { return E{}; }
+ static_assert (false, "foo"_myd); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+ constexpr E operator + (const char *, const E &) { return E{}; }
+ static_assert (false, "foo" + E{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: test" "" { target *-*-* } .-1 }
+ struct H {
+ static constexpr int size () { return 7; }
+ static constexpr const char *data () { return "message"; } };
+ static_assert (true, H{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ static_assert (false, H{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: message" "" { target *-*-* } .-1 }
+ struct I {
+ static constexpr int size () { return 0; }
+ static constexpr const char *data () { return nullptr; } };
+ static_assert (true, I{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ static_assert (false, I{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed" "" { target *-*-* } .-1 }
+#if __cplusplus >= 201402L
+ struct J {
+ static constexpr int size () { return 0; }
+ static constexpr const char *data (int x = 0) { if (x) return nullptr; else throw 1; } }; // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++14 } }
+ static_assert (true, J{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
+ static_assert (false, J{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target { c++14 && c++23_down } } }
+ // { dg-error "'static_assert' message 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } .-1 }
+#endif
+#if __cpp_if_consteval >= 202106L
+ struct K {
+ static constexpr int size () { if consteval { return 4; } else { throw 1; } }
+ static constexpr const char *data () { return "test"; }
+ };
+ static_assert (true, K{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ static_assert (false, K{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ // { dg-error "static assertion failed: test" "" { target c++23 } .-1 }
+ struct L {
+ static constexpr int size () { return 4; }
+ static constexpr const char *data () { if consteval { return "test"; } else { throw 1; } }
+ };
+ static_assert (true, L{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ static_assert (false, L{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ // { dg-error "static assertion failed: test" "" { target c++23 } .-1 }
+ struct M {
+ static constexpr int size () { if consteval { throw 1; } else { return 4; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+ static constexpr const char *data () { return "test"; }
+ };
+ static_assert (true, M{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ static_assert (false, M{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ // { dg-error "'static_assert' message 'size\\\(\\\)' must be a constant expression" "" { target c++23 } .-1 }
+ struct N {
+ static constexpr int size () { return 4; }
+ static constexpr const char *data () { if consteval { throw 1; } else { return "test"; } } // { dg-error "expression '<throw-expression>' is not a constant expression" "" { target c++23 } }
+ };
+ static_assert (true, N{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ static_assert (false, N{}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_only } }
+ // { dg-error "'static_assert' message 'data\\\(\\\)\\\[0\\\]' must be a constant expression" "" { target c++23 } .-1 }
+#endif
+ struct O { constexpr int operator () () const { return 12; } };
+ struct P { constexpr const char *operator () () const { return "another test"; } };
+ struct Q { O size; P data; };
+ static_assert (true, Q ()); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ static_assert (false, Q {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: another test" "" { target *-*-* } .-1 }
+ constexpr int get_size () { return 16; }
+ constexpr const char *get_data () { return "yet another test"; }
+ struct R { int (*size) () = NN::get_size;
+ const char *(*data) () = NN::get_data; };
+ static_assert (true, R ()); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ static_assert (false, R {}); // { dg-warning "'static_assert' with non-string message only available with" "" { target c++23_down } }
+ // { dg-error "static assertion failed: yet another test" "" { target *-*-* } .-1 }
+}
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2023-11-22 10:14:56.057375857 +0100
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2023-11-22 10:17:41.340064988 +0100
@@ -304,8 +304,8 @@
#ifndef __cpp_static_assert
# error "__cpp_static_assert"
-#elif __cpp_static_assert != 201411
-# error "__cpp_static_assert != 201411"
+#elif __cpp_static_assert != 202306
+# error "__cpp_static_assert != 202306"
#endif
#ifndef __cpp_namespace_attributes
--- gcc/testsuite/g++.dg/cpp0x/udlit-error1.C.jj 2023-11-22 10:14:56.037376137 +0100
+++ gcc/testsuite/g++.dg/cpp0x/udlit-error1.C 2023-11-22 10:38:53.298282525 +0100
@@ -11,7 +11,9 @@ void operator""_x(const char *, decltype
#pragma message "hi"_x // { dg-warning "string literal with user-defined suffix is invalid in this context" }
extern "C"_x { void g(); } // { dg-error "before user-defined string literal" }
-static_assert(true, "foo"_x); // { dg-error "string literal with user-defined suffix is invalid in this context|expected" }
+static_assert(true, "foo"_x); // { dg-error "'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 "invalid use of 'void'" "" { target *-*-* } .-2 }
[[deprecated("oof"_x)]] // { dg-error "string literal with user-defined suffix is invalid in this context" "" { target c++26 } }
void
Jakub
next prev parent reply other threads:[~2023-11-23 8:32 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-24 14:30 [PATCH] c++: " Jakub Jelinek
2023-09-18 17:21 ` [PATCH] c++, v2: " Jakub Jelinek
2023-10-27 1:21 ` Jason Merrill
2023-11-17 14:18 ` Jason Merrill
2023-11-17 14:22 ` Jakub Jelinek
2023-11-21 17:17 ` [PATCH] c++, v3: " Jakub Jelinek
2023-11-21 17:23 ` Jakub Jelinek
2023-11-21 21:44 ` Jason Merrill
2023-11-21 22:19 ` Jakub Jelinek
2023-11-21 22:51 ` Jakub Jelinek
2023-11-22 3:51 ` Jason Merrill
2023-11-22 10:00 ` [PATCH] c++, v4: " Jakub Jelinek
2023-11-22 21:53 ` Jason Merrill
2023-11-23 8:32 ` Jakub Jelinek [this message]
2023-11-28 16:31 ` Jason Merrill
2023-11-28 17:52 ` Jakub Jelinek
2023-11-28 21:33 ` Jason Merrill
2023-11-28 17:08 ` 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]) Thomas Schwinge
2023-11-28 17:11 ` Fix 'g++.dg/cpp26/static_assert1.C' for '-fno-exceptions' configurations Jason Merrill
2023-11-29 13:19 ` Thomas Schwinge
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZV8OGMkGJFMG5urh@tucnak \
--to=jakub@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).