@@ -9015,6 +9015,24 @@ struct push_access_scope_guard
}
};
+/* Extracting strings from constexpr. */
+
+class cexpr_str
+{
+public:
+ cexpr_str (tree message) : message(message) {}
+ cexpr_str (const cexpr_str &) = delete;
+ ~cexpr_str () { XDELETEVEC (buf); }
+
+ bool type_check (location_t location);
+ bool extract (location_t location, const char * & msg, int &len);
+ tree message;
+private:
+ tree message_data = NULL_TREE;
+ tree message_sz = NULL_TREE;
+ char *buf = nullptr;
+};
+
/* True if TYPE is an extended floating-point type. */
inline bool
@@ -11558,28 +11558,18 @@ init_cp_semantics (void)
}
-/* Build a STATIC_ASSERT for a static assertion with the condition
- CONDITION and the message text MESSAGE. LOCATION is the location
- of the static assertion in the source code. When MEMBER_P, this
- static assertion is a member of a class. If SHOW_EXPR_P is true,
- print the condition (because it was instantiation-dependent). */
+/* Get constant string from MESSAGE at LOCATION. Returns
+ true if successfull, otherwise false. */
-void
-finish_static_assert (tree condition, tree message, location_t location,
- bool member_p, bool show_expr_p)
+bool
+cexpr_str::type_check (location_t location)
{
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
- || condition == NULL_TREE
- || condition == error_mark_node)
- return;
-
- if (check_for_bare_parameter_packs (condition)
|| check_for_bare_parameter_packs (message))
- return;
+ return false;
if (TREE_CODE (message) != STRING_CST
&& !type_dependent_expression_p (message))
@@ -11595,10 +11585,10 @@ finish_static_assert (tree condition, tree message, location_t location,
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;
+ error_at (location, "constexpr string must be a string "
+ "literal or object with %<size%> and "
+ "%<data%> members");
+ return false;
}
releasing_vec size_args, data_args;
message_sz = finish_call_expr (message_sz, &size_args, false, false,
@@ -11606,26 +11596,144 @@ finish_static_assert (tree condition, tree message, location_t location,
message_data = finish_call_expr (message_data, &data_args, false, false,
complain);
if (message_sz == error_mark_node || message_data == error_mark_node)
- return;
+ return false;
message_sz = build_converted_constant_expr (size_type_node, message_sz,
- complain);
+ complain);
if (message_sz == error_mark_node)
{
- error_at (location, "%<static_assert%> message %<size()%> "
- "must be implicitly convertible to "
- "%<std::size_t%>");
- return;
+ error_at (location, "constexpr string %<size()%> "
+ "must be implicitly convertible to "
+ "%<std::size_t%>");
+ return false;
}
message_data = build_converted_constant_expr (const_string_type_node,
- message_data, complain);
+ message_data, complain);
if (message_data == error_mark_node)
{
- error_at (location, "%<static_assert%> message %<data()%> "
- "must be implicitly convertible to "
- "%<const char*%>");
- return;
+ error_at (location, "constexpr string %<data()%> "
+ "must be implicitly convertible to "
+ "%<const char*%>");
+ return false;
}
}
+ return true;
+}
+
+/* Extract constant string at LOCATION into output string MSG without LEN.
+ Returns true if successfull, otherwise false. */
+
+bool
+cexpr_str::extract (location_t location, const char * & msg, int &len)
+{
+ tsubst_flags_t complain = tf_warning_or_error;
+
+ msg = 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,
+ "constexpr string %<size()%> "
+ "must be a constant expression");
+ return false;
+ }
+ else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz)
+ != tree_to_uhwi (msz))
+ {
+ error_at (location,
+ "constexpr string message %<size()%> "
+ "%qE too large", msz);
+ return false;
+ }
+ 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,
+ "constexpr string %<data()[%d]%> "
+ "must be a constant expression", i);
+ return false;
+ }
+ 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,
+ "constexpr string %<data()%> "
+ "must be a core constant expression");
+ return false;
+ }
+ }
+ }
+ 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;
+ }
+
+ return true;
+}
+
+/* Build a STATIC_ASSERT for a static assertion with the condition
+ CONDITION and the message text MESSAGE. LOCATION is the location
+ of the static assertion in the source code. When MEMBER_P, this
+ static assertion is a member of a class. If SHOW_EXPR_P is true,
+ print the condition (because it was instantiation-dependent). */
+
+void
+finish_static_assert (tree condition, tree message, location_t location,
+ bool member_p, bool show_expr_p)
+{
+ tsubst_flags_t complain = tf_warning_or_error;
+
+ if (condition == NULL_TREE
+ || condition == error_mark_node)
+ return;
+
+ if (check_for_bare_parameter_packs (condition))
+ return;
+
+ cexpr_str cstr(message);
+ if (!cstr.type_check (location))
+ return;
/* Save the condition in case it was a concept check. */
tree orig_condition = condition;
@@ -11638,7 +11746,7 @@ finish_static_assert (tree condition, tree message, location_t location,
defer:
tree assertion = make_node (STATIC_ASSERT);
STATIC_ASSERT_CONDITION (assertion) = orig_condition;
- STATIC_ASSERT_MESSAGE (assertion) = message;
+ STATIC_ASSERT_MESSAGE (assertion) = cstr.message;
STATIC_ASSERT_SOURCE_LOCATION (assertion) = location;
if (member_p)
@@ -11671,88 +11779,8 @@ finish_static_assert (tree condition, tree message, location_t location,
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);
- XDELETEVEC (buf);
- 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;
- }
+ if (!cstr.extract (location, msg, len))
+ return;
/* See if we can find which clause was failing (for logical AND). */
tree bad = find_failing_clause (NULL, orig_condition);
@@ -11768,8 +11796,6 @@ finish_static_assert (tree condition, tree message, location_t location,
else
error_at (cloc, "static assertion failed: %.*s", len, msg);
- XDELETEVEC (buf);
-
diagnose_failing_condition (bad, cloc, show_expr_p);
}
else if (condition && condition != error_mark_node)
@@ -12,7 +12,7 @@ void operator""_x(const char *, decltype(sizeof(0)));
extern "C"_x { void g(); } // { dg-error "before user-defined string literal" }
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 "constexpr string 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 } }
@@ -6,25 +6,25 @@
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 "constexpr string 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 "constexpr string 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 "constexpr string 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 "constexpr string 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 "constexpr string 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 "constexpr string 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 } }
@@ -37,13 +37,13 @@ static_assert (true, E {}); // { dg-warning "'static_assert' with non-string mes
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 "constexpr string '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 "constexpr string '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 ""; } };
@@ -59,7 +59,7 @@ static_assert (true, J (1)); // { dg-warning "'static_assert' with non-string me
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 }
+ // { dg-error "constexpr string '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 {}; }
@@ -72,7 +72,7 @@ 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 }
+ // { dg-error "constexpr string '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; }
@@ -133,7 +133,7 @@ static_assert (false, string_view (4, "testwithextrachars")); // { dg-warning "'
// { 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 }
+ // { dg-error "constexpr string 'data\\\(\\\)\\\[41\\\]' must be a constant expression" "" { target *-*-* } .-2 }
template <typename T, size_t N>
struct array {
@@ -143,7 +143,7 @@ struct array {
};
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 "constexpr string '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 }
@@ -235,7 +235,7 @@ namespace NN
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 "constexpr string 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;
@@ -263,7 +263,7 @@ namespace NN
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 }
+ // { dg-error "constexpr string 'data\\\(\\\)' must be a core constant expression" "" { target c++14 } .-1 }
#endif
#if __cpp_if_consteval >= 202106L
struct K {
@@ -286,14 +286,14 @@ namespace NN
};
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 }
+ // { dg-error "'constexpr string '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 }
+ // { dg-error "'constexpr string '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"; } };