Hi! Here is a slightly updated version of the patch. Fixes the mce_unknown case (sets *non_constant_p in that case so that we e.g. don't try to cache results without emitting diagnostics), the previous patch was incorrect in that part and returned the CALL_EXPR instead of void_node while not setting *non_constant_p. Doesn't implicitly add W in front of the tag when looking it up, I think it is better when people write "Wuninitialized" rather than just "uninitialized". Emits the tag whenever it is non-empty and warning doesn't emit it, in similar style/color to the warning [-Woption] strings. And adds testsuite coverage.
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
