]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Implement __builtin_constexpr_diag (aka P2758 implementation)
authorJakub Jelinek <jakub@redhat.com>
Tue, 17 Feb 2026 14:56:59 +0000 (15:56 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Tue, 17 Feb 2026 14:56:59 +0000 (15:56 +0100)
I know P2758R5 didn't make it into C++26, but on IRC Ville said it would be
useful anyway, so here is a quick attempt at implementing it.

Not adding anything on the libstdc++ side, because I don't know where
experimental stuff like that should go, whether it would be in the
implementation namespace etc.

Tags are currently parsed and printed in the diagnostics, but -Wconstexpr-msg=
option and being able to #pragma GCC diagnostic ignore -Wconstexpr-msg=blah
will need to wait for GCC 17.

In any case, the compiler side is just one new builtin,
__builtin_constexpr_diag, whose first argument is 0 for print,
or 1 for warning or 2 for error (or that ored with 16 if location shouldn't
be caller's location but caller's caller location - useful when adding some
wrappers around the builtin), second argument is string_view of the tag
(or "" for no tag) and third argument is string_view or u8string_view
with the message.  The builtin also handles string literals.

2026-02-17  Jakub Jelinek  <jakub@redhat.com>

gcc/cp/
* cp-tree.h (enum cp_built_in_function): Add
CP_BUILT_IN_CONSTEXPR_DIAG.
(cexpr_str::cexpr_str): Add new default ctor.
(cexpr_str::type_check): Add optional allow_char8_t arg.
(cexpr_str::extract): Add optional ctx, non_constant_p, overflow_p
and jump_target arguments.
* cp-gimplify.cc (cp_gimplify_expr): Throw away
__builtin_constexpr_diag calls after gimplification of
their arguments.
* decl.cc (cxx_init_decl_processing): Create __builtin_constexpr_diag
FE builtin decl.
* constexpr.cc (call_stack, call_stack_tick, last_cx_error_tick):
Moved earlier.
(cxx_eval_constexpr_diag): New function.
(cxx_eval_builtin_function_call): Handle __builtin_constexpr_diag
calls.
* tree.cc (builtin_valid_in_constant_expr_p): Return true for
CP_BUILT_IN_CONSTEXPR_DIAG.
* semantics.cc (cexpr_str::type_check): Add allow_char8_t argument,
if true, allow data to return const char8_t *.
(cexpr_str::extract): Add ctx, non_constant_p, overflow_p and
jump_target arguments, if they are non-NULL, evalute expressions
in that context rather than using cxx_constant_value and translate
back to SOURCE_CHARSET even if message_data or message_sz are NULL.
gcc/testsuite/
* g++.dg/ext/constexpr-diag1.C: New test.
* g++.dg/ext/constexpr-diag2.C: New test.
* g++.dg/ext/constexpr-diag3.C: New test.
* g++.dg/ext/constexpr-diag4.C: New test.
* g++.dg/ext/constexpr-diag5.C: New test.
* g++.dg/ext/constexpr-diag6.C: New test.

12 files changed:
gcc/cp/constexpr.cc
gcc/cp/cp-gimplify.cc
gcc/cp/cp-tree.h
gcc/cp/decl.cc
gcc/cp/semantics.cc
gcc/cp/tree.cc
gcc/testsuite/g++.dg/ext/constexpr-diag1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/constexpr-diag2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/constexpr-diag3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/constexpr-diag4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/constexpr-diag5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/constexpr-diag6.C [new file with mode: 0644]

index 673f4486e42db645fcd2bddac4c89d5c997234ee..44e6b352d9a9a9c4994ae7688fd954b9b961a1fe 100644 (file)
@@ -2314,6 +2314,151 @@ cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call,
     }
 }
 
+/* Variables and functions to manage constexpr call expansion context.
+   These do not need to be marked for PCH or GC.  */
+
+/* FIXME remember and print actual constant arguments.  */
+static vec<tree> call_stack;
+static int call_stack_tick;
+static int last_cx_error_tick;
+
+/* Attempt to evaluate T which represents a call to __builtin_constexpr_diag.
+   The arguments should be an integer (0 for inform, 1 for warning, 2 for
+   error) optionally with 16 ored in if it should use caller's caller location
+   instead of caller's location and 2 messages which are either a pointer to
+   a STRING_CST or class with data () and size () member functions like
+   string_view or u8string_view.  The first message is a tag, with "" passed
+   for no tag, data () should return const char *, the tag should only contain
+   alphanumeric letters or underscores.  The second message is the diagnostic
+   message, data () can be either const char * or const char8_t *.  size ()
+   should return the corresponding length of the strings in bytes as an
+   integer.  */
+
+static tree
+cxx_eval_constexpr_diag (const constexpr_ctx *ctx, tree t, bool *non_constant_p,
+                        bool *overflow_p, tree *jump_target)
+{
+  location_t loc = EXPR_LOCATION (t);
+  if (call_expr_nargs (t) != 3)
+    {
+      if (!ctx->quiet)
+       error_at (loc, "wrong number of arguments to %qs call",
+                 "__builtin_constexpr_diag");
+      *non_constant_p = true;
+      return t;
+    }
+  tree args[3];
+  for (int i = 0; i < 3; ++i)
+    {
+      tree arg = CALL_EXPR_ARG (t, i);
+      arg = cxx_eval_constant_expression (ctx, arg,
+                                         (i == 0
+                                          || POINTER_TYPE_P (TREE_TYPE (arg)))
+                                         ? vc_prvalue : vc_glvalue,
+                                         non_constant_p, overflow_p,
+                                         jump_target);
+      if (*jump_target)
+       return NULL_TREE;
+      if (*non_constant_p)
+       return t;
+      args[i] = arg;
+    }
+  if (TREE_CODE (args[0]) != INTEGER_CST
+      || wi::to_widest (args[0]) < 0
+      || wi::to_widest (args[0]) > 18
+      || (wi::to_widest (args[0]) & 15) > 2)
+    {
+      if (!ctx->quiet)
+       error_at (loc, "first %qs call argument should be 0, 1, 2, 16, 17 or "
+                 "18", "__builtin_constexpr_diag");
+      *non_constant_p = true;
+      return t;
+    }
+  const char *msgs[2] = {};
+  int lens[3] = {};
+  cexpr_str cstrs[2];
+  diagnostics::kind kind = diagnostics::kind::error;
+  for (int i = 1; i < 3; ++i)
+    {
+      tree arg = args[i];
+      if (POINTER_TYPE_P (TREE_TYPE (arg)))
+       {
+         tree str = arg;
+         STRIP_NOPS (str);
+         if (TREE_CODE (str) == ADDR_EXPR
+             && TREE_CODE (TREE_OPERAND (str, 0)) == STRING_CST)
+           {
+             str = TREE_OPERAND (str, 0);
+             tree eltype = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (str)));
+             if (eltype == char_type_node
+                 || (i == 2 && eltype == char8_type_node))
+               arg = str;
+           }
+       }
+      cstrs[i - 1].message = arg;
+      if (!cstrs[i - 1].type_check (loc, i == 2))
+       {
+         *non_constant_p = true;
+         return t;
+       }
+      if (!cstrs[i - 1].extract (loc, msgs[i - 1], lens[i - 1], ctx,
+                                non_constant_p, overflow_p, jump_target))
+       {
+         if (*jump_target)
+           return NULL_TREE;
+         *non_constant_p = true;
+         return t;
+       }
+    }
+  if (msgs[0])
+    {
+      for (int i = 0; i < lens[0]; ++i)
+       if (!ISALNUM (msgs[0][i]) && msgs[0][i] != '_')
+         {
+           if (!ctx->quiet)
+             error_at (loc, "%qs tag string contains %qc character other than"
+                            " letters, digits or %<_%>",
+                       "__builtin_constexpr_diag", msgs[0][i]);
+           *non_constant_p = true;
+           return t;
+         }
+    }
+  if (ctx->manifestly_const_eval == mce_unknown)
+    {
+      *non_constant_p = true;
+      return t;
+    }
+  int arg0 = tree_to_uhwi (args[0]);
+  if (arg0 & 16)
+    {
+      arg0 &= 15;
+      if (!call_stack.is_empty ())
+       {
+         tree call = call_stack.last ();
+         if (EXPR_HAS_LOCATION (call))
+           loc = EXPR_LOCATION (call);
+       }
+    }
+  if (arg0 == 0)
+    kind = diagnostics::kind::note;
+  else if (arg0 == 1)
+    kind = diagnostics::kind::warning;
+  if (lens[0])
+    {
+      const char *color = "error";
+      if (kind == diagnostics::kind::note)
+       color = "note";
+      else if (kind == diagnostics::kind::warning)
+       color = "warning";
+      emit_diagnostic (kind, loc, 0, "constexpr message: %.*s [%r%.*s%R]",
+                      lens[1], msgs[1], color, lens[0], msgs[0]);
+    }
+  else
+    emit_diagnostic (kind, loc, 0, "constexpr message: %.*s",
+                    lens[1], msgs[1]);
+  return void_node;
+}
+
 /* Attempt to evaluate T which represents a call to a builtin function.
    We assume here that all builtin functions evaluate to scalar types
    represented by _CST nodes.  */
@@ -2374,6 +2519,10 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
                                    fun, non_constant_p, overflow_p,
                                    jump_target);
 
+  if (fndecl_built_in_p (fun, CP_BUILT_IN_CONSTEXPR_DIAG, BUILT_IN_FRONTEND))
+    return cxx_eval_constexpr_diag (ctx, t, non_constant_p, overflow_p,
+                                   jump_target);
+
   int strops = 0;
   int strret = 0;
   if (fndecl_built_in_p (fun, BUILT_IN_NORMAL))
@@ -2846,14 +2995,6 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun,
   return binds;
 }
 
-/* Variables and functions to manage constexpr call expansion context.
-   These do not need to be marked for PCH or GC.  */
-
-/* FIXME remember and print actual constant arguments.  */
-static vec<tree> call_stack;
-static int call_stack_tick;
-static int last_cx_error_tick;
-
 static int
 push_cx_call_context (tree call)
 {
index eb30d780d6c05d2a8af033f2b5c1f04d3087cbb1..0b096ccd72718c531690122622a5e85c69065f36 100644 (file)
@@ -986,6 +986,9 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
                                                    &CALL_EXPR_ARG (*expr_p,
                                                                    0));
                break;
+             case CP_BUILT_IN_CONSTEXPR_DIAG:
+               *expr_p = void_node;
+               break;
              default:
                break;
              }
index f205777e1afdd51acfc621b2cc41890c8668d3ed..cc34663b9c60889b9f14bed68c4736c7f89f2dea 100644 (file)
@@ -7112,6 +7112,7 @@ enum cp_built_in_function {
   CP_BUILT_IN_SOURCE_LOCATION,
   CP_BUILT_IN_EH_PTR_ADJUST_REF,
   CP_BUILT_IN_IS_STRING_LITERAL,
+  CP_BUILT_IN_CONSTEXPR_DIAG,
   CP_BUILT_IN_LAST
 };
 
@@ -9549,12 +9550,15 @@ struct push_access_scope_guard
 class cexpr_str
 {
 public:
+  cexpr_str () : message (NULL_TREE) {}
   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);
+  bool type_check (location_t location, bool allow_char8_t = false);
+  bool extract (location_t location, const char * &msg, int &len,
+               const constexpr_ctx * = NULL,  bool * = NULL,
+               bool * = NULL, tree * = NULL);
   bool extract (location_t location, tree &str);
   tree message;
 private:
index 97460e448aeb62cd0bb220190a642294cf0cc31e..6b210a30b6a8a1865844dc9da248d0889c77539d 100644 (file)
@@ -5636,6 +5636,15 @@ cxx_init_decl_processing (void)
                            BUILT_IN_FRONTEND, NULL, NULL_TREE);
   set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF);
 
+  tree void_vaintftype = build_varargs_function_type_list (void_type_node,
+                                                          integer_type_node,
+                                                          NULL_TREE);
+  decl = add_builtin_function ("__builtin_constexpr_diag",
+                              void_vaintftype,
+                              CP_BUILT_IN_CONSTEXPR_DIAG,
+                              BUILT_IN_FRONTEND, NULL, NULL_TREE);
+  set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF);
+
   integer_two_node = build_int_cst (NULL_TREE, 2);
 
   /* Guess at the initial static decls size.  */
index ba5c8fa1b55233b4b0db92ec6b343a4978f6f74d..7f2ea93837039c1fafe170e79e6195a75a229f9d 100644 (file)
@@ -12710,7 +12710,7 @@ init_cp_semantics (void)
    otherwise false.  */
 
 bool
-cexpr_str::type_check (location_t location)
+cexpr_str::type_check (location_t location, bool allow_char8_t /*=false*/)
 {
   tsubst_flags_t complain = tf_warning_or_error;
 
@@ -12746,7 +12746,7 @@ cexpr_str::type_check (location_t location)
       if (message_sz == error_mark_node || message_data == error_mark_node)
        return false;
       message_sz = build_converted_constant_expr (size_type_node, message_sz,
-                                                      complain);
+                                                 complain);
       if (message_sz == error_mark_node)
        {
          error_at (location, "constexpr string %<size()%> "
@@ -12754,8 +12754,17 @@ cexpr_str::type_check (location_t location)
                    "%<std::size_t%>");
          return false;
        }
+
+      if (allow_char8_t
+         && POINTER_TYPE_P (TREE_TYPE (message_data))
+         && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (message_data)))
+             == char8_type_node)
+         && (TYPE_QUALS (TREE_TYPE (TREE_TYPE (message_data)))
+             == TYPE_QUAL_CONST))
+       return true;
+
       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, "constexpr string %<data()%> "
@@ -12785,95 +12794,229 @@ cexpr_str::extract (location_t location, tree &str)
    Returns true if successful, otherwise false.  */
 
 bool
-cexpr_str::extract (location_t location, const char * & msg, int &len)
+cexpr_str::extract (location_t location, const char * &msg, int &len,
+                   const constexpr_ctx *ctx /* = NULL */,
+                   bool *non_constant_p /* = NULL */,
+                   bool *overflow_p /* = NULL */,
+                   tree *jump_target /* = NULL */)
 {
   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);
+      tree msz;
+      if (ctx)
+       {
+         msz = cxx_eval_constant_expression (ctx, message_sz, vc_prvalue,
+                                             non_constant_p, overflow_p,
+                                             jump_target);
+         if (*jump_target || *non_constant_p)
+           return false;
+       }
+      else
+       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");
+         if (!ctx || !cxx_constexpr_quiet_p (ctx))
+           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);
+         if (!ctx || !cxx_constexpr_quiet_p (ctx))
+           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))
+      tree data;
+      if (ctx)
+       {
+         data = cxx_eval_constant_expression (ctx, message_data, vc_prvalue,
+                                              non_constant_p, overflow_p,
+                                              jump_target);
+         if (*jump_target || *non_constant_p)
+           return false;
+         STRIP_NOPS (data);
+         if (TREE_CODE (data) != ADDR_EXPR)
+           {
+           unhandled:
+             if (!cxx_constexpr_quiet_p (ctx))
+               error_at (location, "unhandled return from %<data()%>");
+             return false;
+           }
+         tree str = TREE_OPERAND (data, 0);
+         unsigned HOST_WIDE_INT off = 0;
+         if (TREE_CODE (str) == ARRAY_REF
+             && tree_fits_uhwi_p (TREE_OPERAND (str, 1)))
+           {
+             off = tree_to_uhwi (TREE_OPERAND (str, 1));
+             str = TREE_OPERAND (str, 0);
+           }
+         str = cxx_eval_constant_expression (ctx, str, vc_prvalue,
+                                             non_constant_p, overflow_p,
+                                             jump_target);
+         if (*jump_target || *non_constant_p)
+           return false;
+         if (TREE_CODE (str) == STRING_CST)
+           {
+             if (TREE_STRING_LENGTH (str) < len
+                 || (unsigned) TREE_STRING_LENGTH (str) < off
+                 || (unsigned) TREE_STRING_LENGTH (str) < off + len)
+               goto unhandled;
+             msg = TREE_STRING_POINTER (str) + off;
+             goto translate;
+           }
+         if (TREE_CODE (str) != CONSTRUCTOR
+             || TREE_CODE (TREE_TYPE (str)) != ARRAY_TYPE)
+           goto unhandled;
+         char *b;
+         if (len < 64)
+           b = XALLOCAVEC (char, len + 1);
+         else
+           {
+             buf = XNEWVEC (char, len + 1);
+             b = buf;
+           }
+         msg = b;
+         memset (b, 0, len + 1);
+         tree field, value;
+         unsigned k;
+         unsigned HOST_WIDE_INT l = 0;
+         FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (str), k, field, value)
+           if (!tree_fits_shwi_p (value))
+             goto unhandled;
+           else if (field == NULL_TREE)
+             {
+               if (integer_zerop (value))
+                 break;
+               if (l >= off && l < off + len)
+                 b[l - off] = tree_to_shwi (value);
+               ++l;
+             }
+           else if (TREE_CODE (field) == RANGE_EXPR)
+             {
+               tree lo = TREE_OPERAND (field, 0);
+               tree hi = TREE_OPERAND (field, 1);
+               if (!tree_fits_uhwi_p (lo) || !tree_fits_uhwi_p (hi))
+                 goto unhandled;
+               if (integer_zerop (value))
+                 break;
+               unsigned HOST_WIDE_INT m = tree_to_uhwi (hi);
+               for (l = tree_to_uhwi (lo); l <= m; ++l)
+                 if (l >= off && l < off + len)
+                   b[l - off] = tree_to_shwi (value);
+             }
+           else if (tree_fits_uhwi_p (field))
+             {
+               l = tree_to_uhwi (field);
+               if (integer_zerop (value))
+                 break;
+               if (l >= off && l < off + len)
+                 b[l - off] = tree_to_shwi (value);
+               l++;
+             }
+         b[len] = '\0';
+       }
+      else
+       {
+         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()[%d]%> "
-                           "must be a constant expression", i);
+                           "constexpr string %<data()%> "
+                           "must be a core constant expression");
                  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;
+    }
+translate:
+  if ((message_sz && message_data) || ctx)
+    {
       /* Convert the string from execution charset to SOURCE_CHARSET.  */
       cpp_string istr, ostr;
       istr.len = len;
       istr.text = (const unsigned char *) msg;
+      enum cpp_ttype type = CPP_STRING;
+      if (message_sz && message_data)
+       {
+         if (POINTER_TYPE_P (TREE_TYPE (message_data))
+             && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (message_data)))
+                 == char8_type_node))
+           type = CPP_UTF8STRING;
+       }
+      else if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (message)))
+              == char8_type_node)
+       type = CPP_UTF8STRING;
       if (len == 0)
        ;
-      else if (!cpp_translate_string (parse_in, &istr, &ostr, CPP_STRING,
+      else if (!cpp_translate_string (parse_in, &istr, &ostr, type,
                                      true))
        {
-         error_at (location, "could not convert constexpr string from "
-                             "ordinary literal encoding to source character "
-                             "set");
+         if (type == CPP_UTF8STRING)
+           error_at (location, "could not convert constexpr string from "
+                               "UTF-8 encoding to source character "
+                               "set");
+         else
+           error_at (location, "could not convert constexpr string from "
+                               "ordinary literal encoding to source character "
+                               "set");
          return false;
        }
       else
@@ -12884,13 +13027,6 @@ cexpr_str::extract (location_t location, const char * & msg, int &len)
          len = ostr.len;
        }
     }
-  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;
 }
index 74e387c2d41654bdbf51b88714a16df6e53769d1..20288ed04eb0ad651701cb6d469776294ac8c482 100644 (file)
@@ -569,6 +569,7 @@ builtin_valid_in_constant_expr_p (const_tree decl)
          case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
          case CP_BUILT_IN_EH_PTR_ADJUST_REF:
          case CP_BUILT_IN_IS_STRING_LITERAL:
+         case CP_BUILT_IN_CONSTEXPR_DIAG:
            return true;
          default:
            break;
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag1.C b/gcc/testsuite/g++.dg/ext/constexpr-diag1.C
new file mode 100644 (file)
index 0000000..086784b
--- /dev/null
@@ -0,0 +1,46 @@
+// { dg-do compile { target c++26 } }
+
+struct S {
+  char buf[16];
+  constexpr const char *data () const { return buf; }
+  constexpr decltype (sizeof 0) size () const { for (int i = 0; i < 16; ++i) if (!buf[i]) return i; return 0;  }
+};
+struct T {
+  constexpr const char *data () const { return "bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+constexpr char str[] = "abcdefg";
+struct U {
+  constexpr const char *data () const { return &str[2]; }
+  constexpr decltype (sizeof 0) size () const { return 4; }
+};
+struct V {
+  constexpr const char *data () const { return &"abcdefghi"[3]; }
+  constexpr decltype (sizeof 0) size () const { return 5; }
+};
+struct W {
+  constexpr const char *data () const { return &"abcdefghi"[3] + 2; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+
+consteval
+{
+  S s;
+  for (int i = 0; i < 10; ++i)
+    s.buf[i] = '0' + i;
+  s.buf[10] = '\0';
+  __builtin_constexpr_diag (0, "foo", "bar");          // { dg-message "constexpr message: bar \\\[foo\\\]" }
+  __builtin_constexpr_diag (1, "foo", "bar");          // { dg-warning "constexpr message: bar \\\[foo\\\]" }
+  __builtin_constexpr_diag (2, "foo", "bar");          // { dg-error "constexpr message: bar \\\[foo\\\]" }
+  __builtin_constexpr_diag (0, "bar_baz", "bar");      // { dg-message "constexpr message: bar \\\[bar_baz\\\]" }
+  __builtin_constexpr_diag (1, "bar_baz", "bar");      // { dg-warning "constexpr message: bar \\\[bar_baz\\\]" }
+  __builtin_constexpr_diag (2, "bar_baz", "bar");      // { dg-error "constexpr message: bar \\\[bar_baz\\\]" }
+  __builtin_constexpr_diag (0, "baz", s);              // { dg-message "constexpr message: 0123456789 \\\[baz\\\]" }
+  __builtin_constexpr_diag (1, "baz", T {});           // { dg-warning "constexpr message: bar \\\[baz\\\]" }
+  __builtin_constexpr_diag (2, "baz", U {});           // { dg-error "constexpr message: cdef \\\[baz\\\]" }
+  __builtin_constexpr_diag (0, "baz", V {});           // { dg-message "constexpr message: defgh \\\[baz\\\]" }
+  __builtin_constexpr_diag (1, "baz", W {});           // { dg-warning "constexpr message: fgh \\\[baz\\\]" }
+  __builtin_constexpr_diag (0, "", "bar");             // { dg-message "constexpr message: bar" }
+  __builtin_constexpr_diag (1, "", "bar");             // { dg-warning "constexpr message: bar" }
+  __builtin_constexpr_diag (2, "", "bar");             // { dg-error "constexpr message: bar" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag2.C b/gcc/testsuite/g++.dg/ext/constexpr-diag2.C
new file mode 100644 (file)
index 0000000..21f3f73
--- /dev/null
@@ -0,0 +1,63 @@
+// { dg-do compile { target c++26 } }
+
+#include <string_view>
+
+namespace std
+{
+#if __has_builtin(__builtin_constexpr_diag)
+  struct _S_constexpr_tag_str {
+  private:
+    string_view _M_str;
+  public:
+    template <class _Tp>
+      requires convertible_to<const _Tp&, string_view>
+      consteval _S_constexpr_tag_str(const _Tp& __s) : _M_str(__s) {}
+    friend constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                             string_view) noexcept;
+    friend constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                             u8string_view) noexcept;
+    friend constexpr void constexpr_warning_str(_S_constexpr_tag_str,
+                                               string_view) noexcept;
+    friend constexpr void constexpr_warning_str(_S_constexpr_tag_str,
+                                               u8string_view) noexcept;
+    friend constexpr void constexpr_error_str(_S_constexpr_tag_str,
+                                             string_view) noexcept;
+    friend constexpr void constexpr_error_str(_S_constexpr_tag_str,
+                                             u8string_view) noexcept;
+  };
+  constexpr void constexpr_print_str(string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, "", __msg); }
+  constexpr void constexpr_print_str(u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, "", __msg); }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, __tag._M_str, __msg); }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, __tag._M_str, __msg); }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      string_view __msg) noexcept
+  { return __builtin_constexpr_diag(17, __tag._M_str, __msg); }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(17, __tag._M_str, __msg); }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(18, __tag._M_str, __msg); }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(18, __tag._M_str, __msg); }
+#endif
+}
+
+consteval
+{
+  std::constexpr_print_str("foo");                     // { dg-message "constexpr message: foo" }
+  std::constexpr_print_str(u8"bar");                   // { dg-message "constexpr message: bar" }
+  std::constexpr_print_str("uninitialized", "foo");    // { dg-message "constexpr message: foo \\\[uninitialized\\\]" }
+  std::constexpr_print_str("uninitialized", u8"bar");  // { dg-message "constexpr message: bar \\\[uninitialized\\\]" }
+  std::constexpr_warning_str("uninitialized", "foo");  // { dg-warning "constexpr message: foo \\\[uninitialized\\\]" }
+  std::constexpr_warning_str("uninitialized", u8"bar");        // { dg-warning "constexpr message: bar \\\[uninitialized\\\]" }
+  std::constexpr_error_str("uninitialized", "foo");    // { dg-error "constexpr message: foo \\\[uninitialized\\\]" }
+  std::constexpr_error_str("uninitialized", u8"bar");  // { dg-error "constexpr message: bar \\\[uninitialized\\\]" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag3.C b/gcc/testsuite/g++.dg/ext/constexpr-diag3.C
new file mode 100644 (file)
index 0000000..4001b5c
--- /dev/null
@@ -0,0 +1,64 @@
+// { dg-do compile { target c++26 } }
+
+struct A {
+  constexpr const char *data () const { return "foo"; }
+};
+struct B {
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+struct C {
+  constexpr const char *data () const { return "bar"; }
+  decltype (sizeof 0) size () const { return 3; }
+};
+struct D {
+  const char *data () const { return "bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+struct E {};
+struct F {
+  constexpr const char *data () const { return "bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+struct G {
+  constexpr const char8_t *data () const { return u8"bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+
+consteval { __builtin_constexpr_diag (0); }                    // { dg-error "wrong number of arguments to '__builtin_constexpr_diag' call" }
+consteval { __builtin_constexpr_diag (0, ""); }                        // { dg-error "wrong number of arguments to '__builtin_constexpr_diag' call" }
+consteval { __builtin_constexpr_diag (0, "", "", ""); }                // { dg-error "wrong number of arguments to '__builtin_constexpr_diag' call" }
+consteval { __builtin_constexpr_diag (3, "", ""); }            // { dg-error "first '__builtin_constexpr_diag' call argument should be 0, 1, 2, 16, 17 or 18" }
+consteval { __builtin_constexpr_diag (-42, "", ""); }          // { dg-error "first '__builtin_constexpr_diag' call argument should be 0, 1, 2, 16, 17 or 18" }
+consteval { __builtin_constexpr_diag (1, "abcdABCD_0189", ""); }// { dg-warning "constexpr message:  \\\[abcdABCD_0189\\\]" }
+consteval { __builtin_constexpr_diag (2, "%+-", ""); }         // { dg-error "'__builtin_constexpr_diag' tag string contains '\\\%' character other than letters, digits or '_'" }
+consteval { __builtin_constexpr_diag (0, u8"foo", "bar"); }    // { dg-error "request for member 'size' in" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (1, u8"foo", "bar"); }    // { dg-error "request for member 'size' in" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, u8"foo", "bar"); }    // { dg-error "request for member 'size' in" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (0, "foo", u8"bar"); }    // { dg-message "constexpr message: bar \\\[foo\\\]" }
+consteval { __builtin_constexpr_diag (1, "foo", u8"bar"); }    // { dg-warning "constexpr message: bar \\\[foo\\\]" }
+consteval { __builtin_constexpr_diag (2, "foo", u8"bar"); }    // { dg-error "constexpr message: bar \\\[foo\\\]" }
+consteval { __builtin_constexpr_diag (0, A {}, "foo"); }       // { dg-error "'struct A' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (1, B {}, "foo"); }       // { dg-error "'struct B' has no member named 'data'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, C {}, "foo"); }       // { dg-error "call to non-'constexpr' function '\[^\n\r]* C::size\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (0, D {}, "foo"); }       // { dg-error "call to non-'constexpr' function 'const char\\\* D::data\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (1, E {}, "foo"); }       // { dg-error "'struct E' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, F {}, "foo"); }       // { dg-error "constexpr message: foo \\\[bar\\\]" }
+consteval { __builtin_constexpr_diag (0, G {}, "foo"); }       // { dg-error "conversion from 'const char8_t\\\*' to 'const char\\\*' in a converted constant expression" }
+// { dg-error "could not convert '<anonymous>.G::data\\\(\\\)' from 'const char8_t\\\*' to 'const char\\\*'" "" { target *-*-* } .-1 }
+// { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-2 }
+consteval { __builtin_constexpr_diag (0, "", A {}); }          // { dg-error "'struct A' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (1, "", B {}); }          // { dg-error "'struct B' has no member named 'data'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, "", C {}); }          // { dg-error "call to non-'constexpr' function '\[^\n\r]* C::size\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (0, "", D {}); }          // { dg-error "call to non-'constexpr' function 'const char\\\* D::data\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (1, "", E {}); }          // { dg-error "'struct E' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, "", F {}); }          // { dg-error "constexpr message: bar" }
+consteval { __builtin_constexpr_diag (0, "", G {}); }          // { dg-message "constexpr message: bar" }
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag4.C b/gcc/testsuite/g++.dg/ext/constexpr-diag4.C
new file mode 100644 (file)
index 0000000..6283b05
--- /dev/null
@@ -0,0 +1,48 @@
+// { dg-do compile { target c++26 } }
+// { dg-require-iconv "IBM1047" }
+// { dg-additional-options "-fexec-charset=IBM1047" }
+
+struct S {
+  char buf[16];
+  constexpr const char *data () const { return buf; }
+  constexpr decltype (sizeof 0) size () const { for (int i = 0; i < 16; ++i) if (!buf[i]) return i; return 0;  }
+};
+struct T {
+  constexpr const char *data () const { return "bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+constexpr char str[] = "abcdefg";
+struct U {
+  constexpr const char *data () const { return &str[2]; }
+  constexpr decltype (sizeof 0) size () const { return 4; }
+};
+struct V {
+  constexpr const char *data () const { return &"abcdefghi"[3]; }
+  constexpr decltype (sizeof 0) size () const { return 5; }
+};
+struct W {
+  constexpr const char *data () const { return &"abcdefghi"[3] + 2; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+
+consteval
+{
+  S s;
+  for (int i = 0; i < 10; ++i)
+    s.buf[i] = '0' + i;
+  s.buf[10] = '\0';
+  __builtin_constexpr_diag (0, "foo", "bar");          // { dg-message "constexpr message: bar \\\[foo\\\]" }
+  __builtin_constexpr_diag (1, "foo", "bar");          // { dg-warning "constexpr message: bar \\\[foo\\\]" }
+  __builtin_constexpr_diag (2, "foo", "bar");          // { dg-error "constexpr message: bar \\\[foo\\\]" }
+  __builtin_constexpr_diag (0, "bar_baz", "bar");      // { dg-message "constexpr message: bar \\\[bar_baz\\\]" }
+  __builtin_constexpr_diag (1, "bar_baz", "bar");      // { dg-warning "constexpr message: bar \\\[bar_baz\\\]" }
+  __builtin_constexpr_diag (2, "bar_baz", "bar");      // { dg-error "constexpr message: bar \\\[bar_baz\\\]" }
+  __builtin_constexpr_diag (0, "baz", s);              // { dg-message "constexpr message: 0123456789 \\\[baz\\\]" }
+  __builtin_constexpr_diag (1, "baz", T {});           // { dg-warning "constexpr message: bar \\\[baz\\\]" }
+  __builtin_constexpr_diag (2, "baz", U {});           // { dg-error "constexpr message: cdef \\\[baz\\\]" }
+  __builtin_constexpr_diag (0, "baz", V {});           // { dg-message "constexpr message: defgh \\\[baz\\\]" }
+  __builtin_constexpr_diag (1, "baz", W {});           // { dg-warning "constexpr message: fgh \\\[baz\\\]" }
+  __builtin_constexpr_diag (0, "", "bar");             // { dg-message "constexpr message: bar" }
+  __builtin_constexpr_diag (1, "", "bar");             // { dg-warning "constexpr message: bar" }
+  __builtin_constexpr_diag (2, "", "bar");             // { dg-error "constexpr message: bar" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag5.C b/gcc/testsuite/g++.dg/ext/constexpr-diag5.C
new file mode 100644 (file)
index 0000000..924c7d3
--- /dev/null
@@ -0,0 +1,65 @@
+// { dg-do compile { target c++26 } }
+// { dg-require-iconv "IBM1047" }
+// { dg-additional-options "-fexec-charset=IBM1047" }
+
+#include <string_view>
+
+namespace std
+{
+#if __has_builtin(__builtin_constexpr_diag)
+  struct _S_constexpr_tag_str {
+  private:
+    string_view _M_str;
+  public:
+    template <class _Tp>
+      requires convertible_to<const _Tp&, string_view>
+      consteval _S_constexpr_tag_str(const _Tp& __s) : _M_str(__s) {}
+    friend constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                             string_view) noexcept;
+    friend constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                             u8string_view) noexcept;
+    friend constexpr void constexpr_warning_str(_S_constexpr_tag_str,
+                                               string_view) noexcept;
+    friend constexpr void constexpr_warning_str(_S_constexpr_tag_str,
+                                               u8string_view) noexcept;
+    friend constexpr void constexpr_error_str(_S_constexpr_tag_str,
+                                             string_view) noexcept;
+    friend constexpr void constexpr_error_str(_S_constexpr_tag_str,
+                                             u8string_view) noexcept;
+  };
+  constexpr void constexpr_print_str(string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, "", __msg); }
+  constexpr void constexpr_print_str(u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, "", __msg); }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, __tag._M_str, __msg); }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(16, __tag._M_str, __msg); }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      string_view __msg) noexcept
+  { return __builtin_constexpr_diag(17, __tag._M_str, __msg); }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(17, __tag._M_str, __msg); }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(18, __tag._M_str, __msg); }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(18, __tag._M_str, __msg); }
+#endif
+}
+
+consteval
+{
+  std::constexpr_print_str("foo");                     // { dg-message "constexpr message: foo" }
+  std::constexpr_print_str(u8"bar");                   // { dg-message "constexpr message: bar" }
+  std::constexpr_print_str("uninitialized", "foo");    // { dg-message "constexpr message: foo \\\[uninitialized\\\]" }
+  std::constexpr_print_str("uninitialized", u8"bar");  // { dg-message "constexpr message: bar \\\[uninitialized\\\]" }
+  std::constexpr_warning_str("uninitialized", "foo");  // { dg-warning "constexpr message: foo \\\[uninitialized\\\]" }
+  std::constexpr_warning_str("uninitialized", u8"bar");        // { dg-warning "constexpr message: bar \\\[uninitialized\\\]" }
+  std::constexpr_error_str("uninitialized", "foo");    // { dg-error "constexpr message: foo \\\[uninitialized\\\]" }
+  std::constexpr_error_str("uninitialized", u8"bar");  // { dg-error "constexpr message: bar \\\[uninitialized\\\]" }
+}
diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag6.C b/gcc/testsuite/g++.dg/ext/constexpr-diag6.C
new file mode 100644 (file)
index 0000000..fae3bd3
--- /dev/null
@@ -0,0 +1,66 @@
+// { dg-do compile { target c++26 } }
+// { dg-require-iconv "IBM1047" }
+// { dg-additional-options "-fexec-charset=IBM1047" }
+
+struct A {
+  constexpr const char *data () const { return "foo"; }
+};
+struct B {
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+struct C {
+  constexpr const char *data () const { return "bar"; }
+  decltype (sizeof 0) size () const { return 3; }
+};
+struct D {
+  const char *data () const { return "bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+struct E {};
+struct F {
+  constexpr const char *data () const { return "bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+struct G {
+  constexpr const char8_t *data () const { return u8"bar"; }
+  constexpr decltype (sizeof 0) size () const { return 3; }
+};
+
+consteval { __builtin_constexpr_diag (0); }                    // { dg-error "wrong number of arguments to '__builtin_constexpr_diag' call" }
+consteval { __builtin_constexpr_diag (0, ""); }                        // { dg-error "wrong number of arguments to '__builtin_constexpr_diag' call" }
+consteval { __builtin_constexpr_diag (0, "", "", ""); }                // { dg-error "wrong number of arguments to '__builtin_constexpr_diag' call" }
+consteval { __builtin_constexpr_diag (3, "", ""); }            // { dg-error "first '__builtin_constexpr_diag' call argument should be 0, 1, 2, 16, 17 or 18" }
+consteval { __builtin_constexpr_diag (-42, "", ""); }          // { dg-error "first '__builtin_constexpr_diag' call argument should be 0, 1, 2, 16, 17 or 18" }
+consteval { __builtin_constexpr_diag (1, "abcdABCD_0189", ""); }// { dg-warning "constexpr message:  \\\[abcdABCD_0189\\\]" }
+consteval { __builtin_constexpr_diag (2, "%+-", ""); }         // { dg-error "'__builtin_constexpr_diag' tag string contains '\\\%' character other than letters, digits or '_'" }
+consteval { __builtin_constexpr_diag (0, u8"foo", "bar"); }    // { dg-error "request for member 'size' in" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (1, u8"foo", "bar"); }    // { dg-error "request for member 'size' in" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, u8"foo", "bar"); }    // { dg-error "request for member 'size' in" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (0, "foo", u8"bar"); }    // { dg-message "constexpr message: bar \\\[foo\\\]" }
+consteval { __builtin_constexpr_diag (1, "foo", u8"bar"); }    // { dg-warning "constexpr message: bar \\\[foo\\\]" }
+consteval { __builtin_constexpr_diag (2, "foo", u8"bar"); }    // { dg-error "constexpr message: bar \\\[foo\\\]" }
+consteval { __builtin_constexpr_diag (0, A {}, "foo"); }       // { dg-error "'struct A' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (1, B {}, "foo"); }       // { dg-error "'struct B' has no member named 'data'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, C {}, "foo"); }       // { dg-error "call to non-'constexpr' function '\[^\n\r]* C::size\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (0, D {}, "foo"); }       // { dg-error "call to non-'constexpr' function 'const char\\\* D::data\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (1, E {}, "foo"); }       // { dg-error "'struct E' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, F {}, "foo"); }       // { dg-error "constexpr message: foo \\\[bar\\\]" }
+consteval { __builtin_constexpr_diag (0, G {}, "foo"); }       // { dg-error "conversion from 'const char8_t\\\*' to 'const char\\\*' in a converted constant expression" }
+// { dg-error "could not convert '<anonymous>.G::data\\\(\\\)' from 'const char8_t\\\*' to 'const char\\\*'" "" { target *-*-* } .-1 }
+// { dg-error "constexpr string 'data\\\(\\\)' must be implicitly convertible to 'const char\\\*'" "" { target *-*-* } .-2 }
+consteval { __builtin_constexpr_diag (0, "", A {}); }          // { dg-error "'struct A' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (1, "", B {}); }          // { dg-error "'struct B' has no member named 'data'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, "", C {}); }          // { dg-error "call to non-'constexpr' function '\[^\n\r]* C::size\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (0, "", D {}); }          // { dg-error "call to non-'constexpr' function 'const char\\\* D::data\\\(\\\) const'" }
+consteval { __builtin_constexpr_diag (1, "", E {}); }          // { dg-error "'struct E' has no member named 'size'" }
+// { dg-error "constexpr string must be a string literal or object with 'size' and 'data' members" "" { target *-*-* } .-1 }
+consteval { __builtin_constexpr_diag (2, "", F {}); }          // { dg-error "constexpr message: bar" }
+consteval { __builtin_constexpr_diag (0, "", G {}); }          // { dg-message "constexpr message: bar" }