diff mbox series

[v3,1/2] Factor out static_assert constexpr string extraction for reuse

Message ID 20240605044827.2697253-2-ak@linux.intel.com
State New
Headers show
Series [v3,1/2] Factor out static_assert constexpr string extraction for reuse | expand

Commit Message

Andi Kleen June 5, 2024, 4:45 a.m. UTC
The only semantics changes are slightly more vague error messages
to generalize.

gcc/cp/ChangeLog:

	* cp-tree.h (class cexpr_str): Add.
	* semantics.cc (finish_static_assert): Convert to use cexpr_str.
	(cexpr_str::type_check): Extract constexpr string code to here.
	(cexpr_str::extract): ... and here.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp26/static_assert1.C: Update to new error message.
---
 gcc/cp/cp-tree.h                            |  18 ++
 gcc/cp/semantics.cc                         | 256 +++++++++++---------
 gcc/testsuite/g++.dg/cpp0x/udlit-error1.C   |   2 +-
 gcc/testsuite/g++.dg/cpp26/static_assert1.C |  32 +--
 4 files changed, 176 insertions(+), 132 deletions(-)

Comments

Jason Merrill June 11, 2024, 1:38 p.m. UTC | #1
On 6/5/24 00:45, Andi Kleen wrote:
> The only semantics changes are slightly more vague error messages
> to generalize.

Just a few nits:

> +/* Extracting strings from constexpr.  */
> +
> +class cexpr_str
> +{
> +public:
> +  cexpr_str (tree message) : message(message) {}

Space before paren.

...
> +/* Get constant string from MESSAGE at LOCATION. Returns
> +   true if successfull, otherwise false.  */

This comment needs updating for the interface change.  Only one 'l' in 
"successful".

> -void
> -finish_static_assert (tree condition, tree message, location_t location,
> -		      bool member_p, bool show_expr_p)
...
> +
> +/* Extract constant string at LOCATION into output string MSG without LEN.
> +   Returns true if successfull, otherwise false.  */

"successful" again.

OK with these adjustments.

Jason
diff mbox series

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 565e4a9290e2..3446041937b2 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index f90c304a65b7..5cf698185ba4 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -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)
diff --git a/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C b/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C
index 6d6cd454540a..ea939c52c339 100644
--- a/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C
+++ b/gcc/testsuite/g++.dg/cpp0x/udlit-error1.C
@@ -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 } }
diff --git a/gcc/testsuite/g++.dg/cpp26/static_assert1.C b/gcc/testsuite/g++.dg/cpp26/static_assert1.C
index 59724ae32ce3..7840b6b04d27 100644
--- a/gcc/testsuite/g++.dg/cpp26/static_assert1.C
+++ b/gcc/testsuite/g++.dg/cpp26/static_assert1.C
@@ -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"; } };