On Thu, Dec 18, 2025 at 08:53:20PM +0700, Jason Merrill wrote:
> I think trying to interact with existing warning options is a dead end,
> better to just use -Wconstexpr-msg=tag corresponding to "tag" in the code.

I will work on -Wconstexpr-msg=tag for GCC 17, it will need more work,
because currently the diagnostics code is able to ignore or promote from
warnings to errors only the supported warning options
(or -Werror=warning_opt) but not random user arguments of specific options,
so in order to teach say #pragma GCC diagnostic ignore etc.
about -Wconstexpr-msg=tag it will need separate processing.

That said, here is a simplified version of the last patch which simply
prints stuff always (unless -w for warnings) and promotes warnings to
errors with -Werror only, so tags are just printed and nothing else
is done with those.  I've also removed the handling of = from tags,
so it is really [a-zA-Z0-9_].

On the other side, compared to the last posted patch, this one makes sure
to conver charsets where needed (from exec charset or UTF-8 to
SOURCE_CHARSET).

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-02-11  Jakub Jelinek  <[email protected]>

gcc/cp/
        * cp-tree.h (enum cp_built_in_function): Add
        CP_BUILT_IN_CONSTEXPR_DIAG.
        (cexpr_str::type_check): Add optional allow_char8_t arg.
        (cexpr_str::get_data, cexpr_str::get_sz): New.
        * 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: Include c-family/c-pragma.h.
        (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 *.
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.

--- gcc/cp/cp-tree.h.jj 2026-02-06 11:18:47.077640204 +0100
+++ gcc/cp/cp-tree.h    2026-02-10 12:17:36.083254304 +0100
@@ -7105,6 +7105,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
 };
 
@@ -9543,9 +9544,11 @@ public:
   cexpr_str (const cexpr_str &) = delete;
   ~cexpr_str () { XDELETEVEC (buf); }
 
-  bool type_check (location_t location);
+  bool type_check (location_t location, bool allow_char8_t = false);
   bool extract (location_t location, const char * & msg, int &len);
   bool extract (location_t location, tree &str);
+  tree get_data () const { return message_data; }
+  tree get_sz () const { return message_sz; }
   tree message;
 private:
   tree message_data = NULL_TREE;
--- gcc/cp/cp-gimplify.cc.jj    2026-02-02 10:06:24.445975399 +0100
+++ gcc/cp/cp-gimplify.cc       2026-02-10 12:17:08.549757385 +0100
@@ -978,6 +978,9 @@ cp_gimplify_expr (tree *expr_p, gimple_s
                                                    &CALL_EXPR_ARG (*expr_p,
                                                                    0));
                break;
+             case CP_BUILT_IN_CONSTEXPR_DIAG:
+               *expr_p = void_node;
+               break;
              default:
                break;
              }
--- gcc/cp/decl.cc.jj   2026-02-06 11:18:47.079640171 +0100
+++ gcc/cp/decl.cc      2026-02-10 12:17:08.551261560 +0100
@@ -5612,6 +5612,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.  */
--- gcc/cp/constexpr.cc.jj      2026-02-09 09:06:41.657345262 +0100
+++ gcc/cp/constexpr.cc 2026-02-10 13:09:30.059258053 +0100
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3.
 #include "intl.h"
 #include "toplev.h"
 #include "contracts.h"
+#include "c-family/c-pragma.h"
 
 static bool verify_constant (tree, bool, bool *, bool *);
 #define VERIFY_CONSTANT(X)                                             \
@@ -2314,6 +2315,295 @@ cxx_eval_cxa_builtin_fn (const constexpr
     }
 }
 
+/* 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) 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.  */
+
+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]) > 2)
+    {
+      if (!ctx->quiet)
+       error_at (loc, "first %qs call argument should be 0, 1 or 2",
+                 "__builtin_constexpr_diag");
+      *non_constant_p = true;
+      return t;
+    }
+  const char *msgs[2] = {};
+  bool to_free[2] = {};
+  int lens[3] = {};
+  diagnostics::kind kind = diagnostics::kind::error;
+  for (int i = 1; i < 3; ++i)
+    {
+      tree arg = args[i];
+      bool is_utf8 = false;
+      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;
+                 is_utf8 = eltype == char8_type_node;
+               }
+           }
+       }
+      cexpr_str cstr (arg);
+      if (!cstr.type_check (loc, i == 2))
+       {
+         *non_constant_p = true;
+         return t;
+       }
+      if (TREE_CODE (arg) == STRING_CST)
+       {
+         cstr.extract (loc, msgs[i - 1], lens[i - 1]);
+       translate:
+         /* Convert the string from execution charset resp. UTF-8 to
+            SOURCE_CHARSET.  */
+         cpp_string istr, ostr;
+         istr.len = lens[i - 1];
+         istr.text = (const unsigned char *) msgs[i - 1];
+         if (istr.len == 0)
+           ;
+         else if (!cpp_translate_string (parse_in, &istr, &ostr,
+                                         is_utf8 ? CPP_UTF8STRING
+                                         : CPP_STRING, true))
+           {
+             if (is_utf8)
+               error_at (loc, "could not convert constexpr string from "
+                              "UTF-8 encoding to source character set");
+             else
+               error_at (loc, "could not convert constexpr string from "
+                              "ordinary literal encoding to source "
+                              "character set");
+             *non_constant_p = true;
+             goto out;
+           }
+         else
+           {
+             if (to_free[i - 1])
+               XDELETEVEC (const_cast <char *> (msgs[i - 1]));
+             msgs[i - 1] = (const char *) ostr.text;
+             lens[i - 1] = ostr.len;
+             to_free[i - 1] = true;
+           }
+       }
+      else
+       {
+         /* Can't use cstr.extract because it evaluates the
+            arguments in separate constexpr contexts.  */
+         tree msz = cstr.get_sz ();
+         tree mdata = cstr.get_data ();
+         if (i == 2
+             && POINTER_TYPE_P (TREE_TYPE (mdata))
+             && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (mdata)))
+                 == char8_type_node))
+           is_utf8 = true;
+         msz = cxx_eval_constant_expression (ctx, msz, vc_prvalue,
+                                             non_constant_p, overflow_p,
+                                             jump_target);
+         if (*jump_target)
+           return NULL_TREE;
+         if (*non_constant_p)
+           return t;
+         if (!tree_fits_uhwi_p (msz))
+           {
+             if (!ctx->quiet)
+               error_at (loc, "constexpr string %<size()%> "
+                         "must be a constant expression");
+             *non_constant_p = true;
+             goto out;
+           }
+         else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz)
+                  != tree_to_uhwi (msz))
+           {
+             if (!ctx->quiet)
+               error_at (loc, "constexpr string message %<size()%> "
+                         "%qE too large", msz);
+             *non_constant_p = true;
+             goto out;
+           }
+         lens[i - 1] = tree_to_uhwi (msz);
+         mdata = cxx_eval_constant_expression (ctx, mdata, vc_prvalue,
+                                               non_constant_p, overflow_p,
+                                               jump_target);
+         if (*jump_target)
+           {
+             t = NULL_TREE;
+             goto out;
+           }
+         if (*non_constant_p)
+           goto out;
+         STRIP_NOPS (mdata);
+         if (TREE_CODE (mdata) != ADDR_EXPR)
+           {
+           unhandled:
+             if (!ctx->quiet)
+               error_at (loc, "unhandled return from %<data()%>");
+             *non_constant_p = true;
+             goto out;
+           }
+         tree str = TREE_OPERAND (mdata, 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)
+           {
+             t = NULL_TREE;
+             goto out;
+           }
+         if (*non_constant_p)
+           goto out;
+         if (TREE_CODE (str) == STRING_CST)
+           {
+             if (TREE_STRING_LENGTH (str) < lens[i - 1]
+                 || (unsigned) TREE_STRING_LENGTH (str) < off
+                 || (unsigned) TREE_STRING_LENGTH (str) < off + lens[i - 1])
+               goto unhandled;
+             msgs[i - 1] = TREE_STRING_POINTER (str) + off;
+             goto translate;
+           }
+         if (TREE_CODE (str) != CONSTRUCTOR
+             || TREE_CODE (TREE_TYPE (str)) != ARRAY_TYPE)
+           goto unhandled;
+         char *buf;
+         if (lens[i - 1] < 64)
+           buf = XALLOCAVEC (char, lens[i - 1] + 1);
+         else
+           {
+             buf = XNEWVEC (char, lens[i - 1] + 1);
+             to_free[i - 1] = true;
+           }
+         msgs[i - 1] = buf;
+         memset (buf, 0, lens[i - 1] + 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 + lens[i - 1])
+                 buf[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 + lens[i - 1])
+                   buf[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 + lens[i - 1])
+                 buf[l - off] = tree_to_shwi (value);
+               l++;
+             }
+         buf[lens[i - 1]] = '\0';
+         goto translate;
+       }
+    }
+  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;
+           goto out;
+         }
+    }
+  if (ctx->manifestly_const_eval == mce_unknown)
+    {
+      *non_constant_p = true;
+      goto out;
+    }
+  if (integer_zerop (args[0]))
+    kind = diagnostics::kind::note;
+  else if (integer_onep (args[0]))
+    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]);
+  t = void_node;
+out:
+  if (to_free[0])
+    XDELETEVEC (const_cast <char *> (msgs[0]));
+  if (to_free[1])
+    XDELETEVEC (const_cast <char *> (msgs[1]));
+  return t;
+}
+
 /* 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 +2664,10 @@ cxx_eval_builtin_function_call (const co
                                    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))
--- gcc/cp/tree.cc.jj   2026-02-07 11:09:20.173196298 +0100
+++ gcc/cp/tree.cc      2026-02-10 12:18:31.699307653 +0100
@@ -569,6 +569,7 @@ builtin_valid_in_constant_expr_p (const_
          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;
--- gcc/cp/semantics.cc.jj      2026-02-06 11:18:47.089640002 +0100
+++ gcc/cp/semantics.cc 2026-02-10 12:17:08.554709098 +0100
@@ -12706,7 +12706,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;
 
@@ -12742,7 +12742,7 @@ cexpr_str::type_check (location_t locati
       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()%> "
@@ -12750,8 +12750,17 @@ cexpr_str::type_check (location_t locati
                    "%<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()%> "
--- gcc/testsuite/g++.dg/ext/constexpr-diag1.C.jj       2026-02-10 
12:17:08.588592279 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag1.C  2026-02-10 12:39:59.090395845 
+0100
@@ -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" }
+}
--- gcc/testsuite/g++.dg/ext/constexpr-diag2.C.jj       2026-02-10 
12:17:08.588696015 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag2.C  2026-02-10 12:39:48.388578011 
+0100
@@ -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(0, "", __msg); }                   // { 
dg-message "constexpr message: foo" }
+  constexpr void constexpr_print_str(u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(0, "", __msg); }                   // { 
dg-message "constexpr message: bar" }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(0, __tag._M_str, __msg); }         // { 
dg-message "constexpr message: foo \\\[uninitialized\\\]" }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(0, __tag._M_str, __msg); }         // { 
dg-message "constexpr message: bar \\\[uninitialized\\\]" }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      string_view __msg) noexcept
+  { return __builtin_constexpr_diag(1, __tag._M_str, __msg); }         // { 
dg-warning "constexpr message: foo \\\[uninitialized\\\]" }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(1, __tag._M_str, __msg); }         // { 
dg-warning "constexpr message: bar \\\[uninitialized\\\]" }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(2, __tag._M_str, __msg); }         // { 
dg-error "constexpr message: foo \\\[uninitialized\\\]" }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(2, __tag._M_str, __msg); }         // { 
dg-error "constexpr message: bar \\\[uninitialized\\\]" }
+#endif
+}
+
+consteval
+{
+  std::constexpr_print_str("foo");
+  std::constexpr_print_str(u8"bar");
+  std::constexpr_print_str("uninitialized", "foo");
+  std::constexpr_print_str("uninitialized", u8"bar");
+  std::constexpr_warning_str("uninitialized", "foo");
+  std::constexpr_warning_str("uninitialized", u8"bar");
+  std::constexpr_error_str("uninitialized", "foo");
+  std::constexpr_error_str("uninitialized", u8"bar");
+}
--- gcc/testsuite/g++.dg/ext/constexpr-diag3.C.jj       2026-02-10 
12:17:08.589234523 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag3.C  2026-02-10 12:39:38.614744382 
+0100
@@ -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 or 2" }
+consteval { __builtin_constexpr_diag (-42, "", ""); }          // { dg-error 
"first '__builtin_constexpr_diag' call argument should be 0, 1 or 2" }
+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" }
--- gcc/testsuite/g++.dg/ext/constexpr-diag4.C.jj       2026-02-10 
13:14:14.305424386 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag4.C  2026-02-10 13:14:26.434218136 
+0100
@@ -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" }
+}
--- gcc/testsuite/g++.dg/ext/constexpr-diag5.C.jj       2026-02-10 
13:14:52.983766656 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag5.C  2026-02-10 13:15:08.370505001 
+0100
@@ -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(0, "", __msg); }                   // { 
dg-message "constexpr message: foo" }
+  constexpr void constexpr_print_str(u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(0, "", __msg); }                   // { 
dg-message "constexpr message: bar" }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(0, __tag._M_str, __msg); }         // { 
dg-message "constexpr message: foo \\\[uninitialized\\\]" }
+  constexpr void constexpr_print_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(0, __tag._M_str, __msg); }         // { 
dg-message "constexpr message: bar \\\[uninitialized\\\]" }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      string_view __msg) noexcept
+  { return __builtin_constexpr_diag(1, __tag._M_str, __msg); }         // { 
dg-warning "constexpr message: foo \\\[uninitialized\\\]" }
+  constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag,
+                                      u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(1, __tag._M_str, __msg); }         // { 
dg-warning "constexpr message: bar \\\[uninitialized\\\]" }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    string_view __msg) noexcept
+  { return __builtin_constexpr_diag(2, __tag._M_str, __msg); }         // { 
dg-error "constexpr message: foo \\\[uninitialized\\\]" }
+  constexpr void constexpr_error_str(_S_constexpr_tag_str __tag,
+                                    u8string_view __msg) noexcept
+  { return __builtin_constexpr_diag(2, __tag._M_str, __msg); }         // { 
dg-error "constexpr message: bar \\\[uninitialized\\\]" }
+#endif
+}
+
+consteval
+{
+  std::constexpr_print_str("foo");
+  std::constexpr_print_str(u8"bar");
+  std::constexpr_print_str("uninitialized", "foo");
+  std::constexpr_print_str("uninitialized", u8"bar");
+  std::constexpr_warning_str("uninitialized", "foo");
+  std::constexpr_warning_str("uninitialized", u8"bar");
+  std::constexpr_error_str("uninitialized", "foo");
+  std::constexpr_error_str("uninitialized", u8"bar");
+}
--- gcc/testsuite/g++.dg/ext/constexpr-diag6.C.jj       2026-02-10 
13:15:25.165219406 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag6.C  2026-02-10 13:15:29.113152271 
+0100
@@ -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 or 2" }
+consteval { __builtin_constexpr_diag (-42, "", ""); }          // { dg-error 
"first '__builtin_constexpr_diag' call argument should be 0, 1 or 2" }
+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" }


        Jakub

Reply via email to