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
