On 2/11/26 9:19 AM, Jakub Jelinek wrote:
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"
Is this still needed for this version?
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. */
Please explain the significance of the two messages as well. And also
the expected types.
Why is the tag string before the message string, since the tag is the
one that's sometimes omitted?
+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. */
Didn't I already suggest adding another overload that takes a
constexpr_ctx? This is too much code to duplicate in a user.
+ 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" }
Hmm, it would be more useful if the messages were at the call site of
this function rather than at the definition. I guess you can get that
location from call_stack. But this can be a followup.
+ 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