On 9/18/23 13:21, Jakub Jelinek wrote:
Here is an updated version of the patch.
Compared to the last version, based on the discussion in the PR, the patch
1) warns (but only that) if size()/data() methods aren't declared
constexpr/consteval (or implicitly constexpr)
The language requirements also seem to be satisfied by
constexpr const char msg[] = "foo";
struct A { constexpr int operator () () { return sizeof(msg); } };
struct B { constexpr const char * operator()() { return msg; } };
struct C {
A size;
B data;
};
constexpr int i = C().size();
constexpr const char *p = C().data();
static_assert (false, C());
constexpr int size() { return sizeof(msg); }
constexpr const char *data() { return msg; }
struct D {
int (*size)() = ::size;
const char *(*data)() = ::data;
};
constexpr int di = D().size();
constexpr const char *dp = D().data();
static_assert (false, D());
so we shouldn't assume that size/data are methods.
2) as I don't see a function which would determine if some expression
is core constant expression (for the data() case), the patch just as an
optimization tries to fold_nondependent_expr msg.data() expression
quietly, if it is a constant expression, passes it to c_getstr if len > 0
and if successful, only tries to constant expression evaluate
msg.data()[0] and msg.data()[len - 1], otherwise it will constant
expression evaluate the characters one by one;
for the len == 0 case, it will fold_nondependent_expr + check result is
integer_zero_node for (msg.data(), 0) which I think should fail if
msg.data() is not a core constant expression, but succeed if it is
even if it is not constant expression
Sounds good.
3) already the earlier version of the patch was passing
manifestly_const_eval=true argument, you said in the PR you've raised
it in CWG, I've newly added testsuite coverage for that
CWG agreed with this direction.
--- gcc/cp/semantics.cc.jj 2023-09-05 17:26:51.849921954 +0200
+++ gcc/cp/semantics.cc 2023-09-18 14:31:55.269431759 +0200
@@ -11388,11 +11389,77 @@ 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);
This should probably use finish_call_expr instead of
build_new_method_call because of my example above, and also so you don't
need to pull out the TREE_OPERAND or check the result of name lookup.
+ 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);
Likewise.
+ 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");
This diagnostic should be just if the calls to
finish_class_member_access_expr fail; better to get the normal
diagnostics from finish_call_expr if the calls fail for whatever reason.
+ return;
+ }
+ if (tree s
+ = cp_get_callee_fndecl_nofold (extract_call_expr (message_sz)))
+ if (!DECL_DECLARED_CONSTEXPR_P (s))
+ warning_at (location, 0, "%<static_assert%> message %qs "
+ "member not %<constexpr%>", "size()");
+ message_sz = perform_implicit_conversion (size_type_node, message_sz,
+ tf_none);
This should probably use build_converted_constant_expr?
+ if (message_sz == error_mark_node)
+ {
+ error_at (location, "%<static_assert%> message %<size()%> member "
+ "function must be implicitly convertible to "
+ "%<std::size_t%>");
+ return;
+ }
+ if (tree d
+ = cp_get_callee_fndecl_nofold (extract_call_expr (message_data)))
+ if (!DECL_DECLARED_CONSTEXPR_P (d))
+ warning_at (location, 0, "%<static_assert%> message %qs "
+ "member not %<constexpr%>", "data()");
+ 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. */
@@ -11430,9 +11497,88 @@ 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");
This can use cxx_constant_value to show what makes it not a
constant-expression. And also don't assume size is a member function.
+ return;
+ }
+ len = tree_to_uhwi (message_sz);
+ tree data
+ = fold_non_dependent_expr (message_data, tf_none,
+ /*manifestly_const_eval=*/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);
+ 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");
Likewise.
+ 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 if (!data)
+ {
+ data = build2 (COMPOUND_EXPR, integer_type_node,
+ message_data, integer_zero_node);
+ data = fold_non_dependent_expr (data, complain,
+ /*manifestly_const_eval=*/
+ true);
+ if (!integer_zerop (data))
+ {
+ error_at (location,
+ "%<static_assert%> message %<data()%> "
+ "member function must be a constant "
+ "expression");
Likewise.
+ 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);
@@ -11442,12 +11588,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-09-18 12:42:16.309387948 +0200
+++ gcc/cp/pt.cc 2023-09-18 13:09:47.166440918 +0200
@@ -19430,15 +19430,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);
}
--- gcc/testsuite/g++.dg/cpp26/static_assert1.C.jj 2023-09-18
13:09:47.167440904 +0200
+++ gcc/testsuite/g++.dg/cpp26/static_assert1.C 2023-09-18 15:04:25.402596093
+0200
@@ -0,0 +1,284 @@
+// 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 }
The diagnostic states properties that E satisfies, and not the problem
(that E{}.size isn't invocable).
+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 } }
+ // { dg-warning "'static_assert' message 'size\\\(\\\)' member
not 'constexpr'" "" { target *-*-* } .-1 }
+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 } }
+ // { dg-warning "'static_assert' message 'data\\\(\\\)' member
not 'constexpr'" "" { target *-*-* } .-1 }
+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 }
The cxx_constant_value diagnostics should be helpful here.
+#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 }
The cxx_constant_value diagnostics should be helpful here.
+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
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 "'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 }
More cases of the diagnostic not stating the problem; as above, this
message should only be for when lookup fails.
+#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 }
+ 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; } };
+ 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\\\(\\\)'
member function must be a 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;
} }
+ 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\\\(\\\)'
member function 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"; } }
+ };
+ 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\\\(\\\)'
member function must be a constant expression" "" { target c++23 } .-1 }
+#endif
+}
--- gcc/testsuite/g++.dg/cpp26/feat-cxx26.C.jj 2023-09-18 12:42:16.327387707
+0200
+++ gcc/testsuite/g++.dg/cpp26/feat-cxx26.C 2023-09-18 13:09:47.167440904
+0200
@@ -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-09-18
13:08:31.530448184 +0200
+++ gcc/testsuite/g++.dg/cpp0x/udlit-error1.C 2023-09-18 13:09:47.167440904
+0200
@@ -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 } }
This diagnostic message seems unclear for a UDL?
+ // { 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
Jakub