Hi!
The following patch on top of PR110349 patch (weak dependency,
only for -Wc++26-extensions, I could split that part into an independent
patch) and PR110342 patch (again weak dependency, this time mainly
because it touches the same code in cp_parser_static_assert and
nearby spot in udlit-error1.C testcase) 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 4 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 and most important is that clang++ has a couple of tests (and the
testcase below as well) where M.data () is not a core constant expression
but M.data ()[0] ... M.data ()[M.size () - 1] is integer constant
expression. From my reading of http://eel.is/c++draft/dcl.pre#11.2.2
that means those should be rejected (examples of these are e.g.
static_assert (false, T{});
in the testcase, where T{}.data () returns pointer returned from new
expression, but T{}'s destructor then deletes it, making it point to
no longer live object. Or
static_assert (false, a);
where a.data () returns &a.a but because a is constexpr automatic variable,
that isn't valid core constant expression, while a.data ()[0] is.
There are a couple of others. Now, it seems allowing that is quite useful
in real-world, but the question is with what standard changes to achieve
that. One possibility would be s/a core constant/an/; from implementation
POV that would mean that if M.size () is 0, then M.data () doesn't have
to be constexpr at all. Otherwise, implementation could try to evaluate
silently M.data () as constant expression, if it would be one, it could
just use c_getstr in the GCC case as the patch does + optionally the 2
M.data ()[0] and M.data ()[M.size () - 1] tests to verify boundary cases
more carefully. And if it wouldn't be one, it would need to evaluate
M.data ()[i] for i in [0, M.size () - 1] to get all the characters one by
one. Another possibility would be to require that say ((void) (M.data ()), 0)
is a constant expression, that doesn't help much with the optimized way
to get at the message characters, but would require that data () is
constexpr even for the 0 case etc.
The third 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 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 patch uses tf_none in 6 spots so that just the static_assert specific
errors are emitted and not others, but it would certainly be possible to
use complain instead of tf_none there, get more errors in some cases, but
perhaps help users figure out what exactly is wrong in detail.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2023-08-24 Jakub Jelinek <jakub@redhat.com>
PR c++/110348
gcc/c-family/
* 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.
* 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.
Jakub
@@ -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");
@@ -1087,6 +1088,7 @@ c_cpp_builtins (cpp_reader *pfile)
/* Set feature test macros for C++26. */
cpp_define (pfile, "__cpp_constexpr=202306L");
cpp_define (pfile, "__cpp_placeholder_variables=202306L");
+ cpp_define (pfile, "__cpp_static_assert=202306L");
}
if (flag_concepts)
{
@@ -16427,6 +16427,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. */
@@ -16457,10 +16458,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)
{
@@ -16479,8 +16480,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,
@@ -11337,6 +11337,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
@@ -11346,11 +11347,67 @@ finish_static_assert (tree condition, tr
if (check_for_bare_parameter_packs (condition))
condition = error_mark_node;
+ if (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, tf_none);
+ if (TREE_CODE (message_sz) != COMPONENT_REF)
+ message_sz = error_mark_node;
+ if (message_sz != error_mark_node)
+ message_sz = build_new_method_call (message,
+ TREE_OPERAND (message_sz, 1),
+ NULL, NULL_TREE, LOOKUP_NORMAL,
+ NULL, tf_none);
+ message_data
+ = finish_class_member_access_expr (message,
+ get_identifier ("data"),
+ false, tf_none);
+ if (TREE_CODE (message_data) != COMPONENT_REF)
+ message_data = error_mark_node;
+ if (message_data != error_mark_node)
+ message_data = build_new_method_call (message,
+ TREE_OPERAND (message_data, 1),
+ NULL, NULL_TREE, LOOKUP_NORMAL,
+ NULL, tf_none);
+ 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;
+ }
+ message_sz = perform_implicit_conversion (size_type_node, message_sz,
+ tf_none);
+ if (message_sz == error_mark_node)
+ {
+ error_at (location, "%<static_assert%> message %<size()%> member "
+ "function must be implicitly convertible to "
+ "%<std::size_t%>");
+ return;
+ }
+ message_data = perform_implicit_conversion (const_string_type_node,
+ message_data, tf_none);
+ if (message_data == error_mark_node)
+ {
+ error_at (location, "%<static_assert%> message %<data()%> member "
+ "function 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. */
@@ -11388,9 +11445,76 @@ 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)
+ {
+ message_sz
+ = fold_non_dependent_expr (message_sz, complain,
+ /*manifestly_const_eval=*/true);
+ if (!tree_fits_uhwi_p (message_sz)
+ || ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (message_sz)
+ != tree_to_uhwi (message_sz)))
+ {
+ error_at (location,
+ "%<static_assert%> message %<size()%> member "
+ "function must be a constant expression");
+ return;
+ }
+ len = tree_to_uhwi (message_sz);
+ message_data
+ = fold_non_dependent_expr (message_data, complain,
+ /*manifestly_const_eval=*/true);
+ if (!reduced_constant_expression_p (message_data))
+ {
+ error_at (location,
+ "%<static_assert%> message %<data()%> member "
+ "function must be a constant expression");
+ return;
+ }
+ if (len)
+ {
+ msg = c_getstr (message_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);
+ t = fold_non_dependent_expr (t, complain,
+ /*manifestly_const_eval=*/
+ true);
+ if (!tree_fits_shwi_p (t))
+ {
+ error_at (location,
+ "%<static_assert%> message %<data()%> "
+ "member function must be a constant "
+ "expression");
+ return;
+ }
+ if (msg == NULL)
+ buf[i] = tree_to_shwi (t);
+ /* 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
+ {
+ 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);
@@ -11404,8 +11528,9 @@ finish_static_assert (tree condition, tr
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);
}
@@ -19429,15 +19429,20 @@ tsubst_expr (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);
}
@@ -0,0 +1,238 @@
+// 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 }
+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 }
+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 }
+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 }
+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 }
+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 }
+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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { 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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { target *-*-* } .-1 }
+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\\\(\\\)' member function must be implicitly convertible to 'std::size_t'" "" { target *-*-* } .-1 }
+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\\\(\\\)' member function must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 }
+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; }
+ 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\\\(\\\)' member function 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' }; } };
+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\\\(\\\)' member function 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 "'static_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { target *-*-* } .-1 }
+
+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\\\(\\\)' member function must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-1 }
+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_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { 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_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { 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_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { 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_assert' message 'data\\\(\\\)' member function must be a constant expression" "" { 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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { 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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { 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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { 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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { 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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { 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 "'static_assert' message must be a string literal or object with 'size\\\(\\\)' and 'data\\\(\\\)' members" "" { target *-*-* } .-1 }
+#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 }
+ 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 }
+}
@@ -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
@@ -11,7 +11,8 @@ 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 }
[[deprecated("oof"_x)]] // { dg-error "string literal with user-defined suffix is invalid in this context" "" { target c++26 } }
void