Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
Note, haven't added user documentation for this because it might change
behavior before C++29 gets something voted in, or perhaps have
the custom tags support added or whatever.
2025-11-12 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 (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.
--- gcc/cp/cp-tree.h.jj 2025-11-12 18:23:48.790468748 +0100
+++ gcc/cp/cp-tree.h 2025-11-12 18:24:25.463278871 +0100
@@ -6894,6 +6894,7 @@ enum cp_built_in_function {
CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
CP_BUILT_IN_SOURCE_LOCATION,
CP_BUILT_IN_EH_PTR_ADJUST_REF,
+ CP_BUILT_IN_CONSTEXPR_DIAG,
CP_BUILT_IN_LAST
};
@@ -9245,9 +9246,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 2025-11-12 18:23:48.789468762 +0100
+++ gcc/cp/cp-gimplify.cc 2025-11-12 18:24:25.466907281 +0100
@@ -956,6 +956,9 @@ cp_gimplify_expr (tree *expr_p, gimple_s
"__builtin_eh_ptr_adjust_ref");
*expr_p = void_node;
break;
+ case CP_BUILT_IN_CONSTEXPR_DIAG:
+ *expr_p = void_node;
+ break;
default:
break;
}
--- gcc/cp/decl.cc.jj 2025-11-12 18:23:48.792468720 +0100
+++ gcc/cp/decl.cc 2025-11-12 18:24:25.465689864 +0100
@@ -5574,6 +5574,15 @@ cxx_init_decl_processing (void)
set_call_expr_flags (decl, 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 2025-11-12 18:23:48.767469067 +0100
+++ gcc/cp/constexpr.cc 2025-11-12 19:11:04.085138360 +0100
@@ -2262,6 +2262,268 @@ 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] = {};
+ int opt_index = OPT_SPECIAL_unknown;
+ 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;
+ }
+ }
+ 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]);
+ 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 ();
+ 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;
+ continue;
+ }
+ 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';
+ }
+ }
+ if (msgs[0])
+ {
+ for (int i = 0; i < lens[0]; ++i)
+ if (!ISALNUM (msgs[0][i]) && 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 (msgs[0] && lens[0])
+ {
+ char *new_option = XNEWVEC (char, lens[0] + 1);
+ memcpy (new_option, msgs[0], lens[0]);
+ new_option[lens[0]] = '\0';
+ opt_index = find_opt (new_option, CL_CXX);
+ XDELETEVEC (new_option);
+ if (opt_index != OPT_SPECIAL_unknown
+ && !(cl_options[opt_index].flags & CL_WARNING))
+ opt_index = OPT_SPECIAL_unknown;
+ }
+ if (integer_zerop (args[0]))
+ kind = diagnostics::kind::note;
+ else if (integer_onep (args[0]))
+ kind = diagnostics::kind::warning;
+ if (opt_index != OPT_SPECIAL_unknown && kind == diagnostics::kind::warning)
+ emit_diagnostic (kind, loc, opt_index, "constexpr message: %.*s",
+ lens[1], msgs[1]);
+ else 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. */
@@ -2322,6 +2584,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 2025-11-12 18:23:48.793468706 +0100
+++ gcc/cp/tree.cc 2025-11-12 18:24:25.459960406 +0100
@@ -489,6 +489,7 @@ builtin_valid_in_constant_expr_p (const_
case CP_BUILT_IN_IS_CORRESPONDING_MEMBER:
case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
case CP_BUILT_IN_EH_PTR_ADJUST_REF:
+ case CP_BUILT_IN_CONSTEXPR_DIAG:
return true;
default:
break;
--- gcc/cp/semantics.cc.jj 2025-11-12 18:23:48.793468706 +0100
+++ gcc/cp/semantics.cc 2025-11-12 18:24:25.464252942 +0100
@@ -12493,7 +12493,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;
@@ -12529,7 +12529,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()%> "
@@ -12537,8 +12537,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 2025-11-12
18:45:16.433607544 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag1.C 2025-11-12 18:53:18.465917008
+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, "Wformat=", "bar"); // { dg-message "constexpr
message: bar \\\[Wformat=\\\]" }
+ __builtin_constexpr_diag (1, "Wformat=", "bar");
+ __builtin_constexpr_diag (2, "Wformat=", "bar"); // { dg-error "constexpr
message: bar \\\[Wformat=\\\]" }
+ __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 2025-11-12
18:45:19.579563878 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag2.C 2025-11-12 18:57:11.963676082
+0100
@@ -0,0 +1,64 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-Wuninitialized" }
+
+#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 \\\[Wuninitialized\\\]" }
+ 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 \\\[Wuninitialized\\\]" }
+ 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 \\\[-Wuninitialized\\\]" }
+ 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 \\\[-Wuninitialized\\\]" }
+ 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 \\\[Wuninitialized\\\]" }
+ 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 \\\[Wuninitialized\\\]" }
+#endif
+}
+
+consteval
+{
+ std::constexpr_print_str("foo");
+ std::constexpr_print_str(u8"bar");
+ std::constexpr_print_str("Wuninitialized", "foo");
+ std::constexpr_print_str("Wuninitialized", u8"bar");
+ std::constexpr_warning_str("Wuninitialized", "foo");
+ std::constexpr_warning_str("Wuninitialized", u8"bar");
+ std::constexpr_error_str("Wuninitialized", "foo");
+ std::constexpr_error_str("Wuninitialized", u8"bar");
+}
--- gcc/testsuite/g++.dg/ext/constexpr-diag3.C.jj 2025-11-12
18:45:34.674354365 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag3.C 2025-11-12 18:53:51.911452783
+0100
@@ -0,0 +1,47 @@
+// { dg-do compile { target c++26 } }
+// { dg-additional-options "-Wformat=2" }
+
+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, "Wformat=", "bar"); // { dg-message "constexpr
message: bar \\\[Wformat=\\\]" }
+ __builtin_constexpr_diag (1, "Wformat=", "bar"); // { dg-warning "constexpr
message: bar \\\[-Wformat=\\\]" }
+ __builtin_constexpr_diag (2, "Wformat=", "bar"); // { dg-error "constexpr
message: bar \\\[Wformat=\\\]" }
+ __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-diag4.C.jj 2025-11-12
18:58:44.709388779 +0100
+++ gcc/testsuite/g++.dg/ext/constexpr-diag4.C 2025-11-12 19:19:48.401871274
+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" }
Jakub