From: Jakub Jelinek Date: Tue, 17 Feb 2026 14:56:59 +0000 (+0100) Subject: c++: Implement __builtin_constexpr_diag (aka P2758 implementation) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5932fb71d5ccbcd6119e4e9eae315d4a8795726f;p=thirdparty%2Fgcc.git c++: Implement __builtin_constexpr_diag (aka P2758 implementation) I know P2758R5 didn't make it into C++26, but on IRC Ville said it would be useful anyway, so here is a quick attempt at implementing it. Not adding anything on the libstdc++ side, because I don't know where experimental stuff like that should go, whether it would be in the implementation namespace etc. Tags are currently parsed and printed in the diagnostics, but -Wconstexpr-msg= option and being able to #pragma GCC diagnostic ignore -Wconstexpr-msg=blah will need to wait for GCC 17. In any case, the compiler side is just one new builtin, __builtin_constexpr_diag, whose first argument is 0 for print, or 1 for warning or 2 for error (or that ored with 16 if location shouldn't be caller's location but caller's caller location - useful when adding some wrappers around the builtin), second argument is string_view of the tag (or "" for no tag) and third argument is string_view or u8string_view with the message. The builtin also handles string literals. 2026-02-17 Jakub Jelinek gcc/cp/ * cp-tree.h (enum cp_built_in_function): Add CP_BUILT_IN_CONSTEXPR_DIAG. (cexpr_str::cexpr_str): Add new default ctor. (cexpr_str::type_check): Add optional allow_char8_t arg. (cexpr_str::extract): Add optional ctx, non_constant_p, overflow_p and jump_target arguments. * 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 (call_stack, call_stack_tick, last_cx_error_tick): Moved earlier. (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 *. (cexpr_str::extract): Add ctx, non_constant_p, overflow_p and jump_target arguments, if they are non-NULL, evalute expressions in that context rather than using cxx_constant_value and translate back to SOURCE_CHARSET even if message_data or message_sz are NULL. 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. --- diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 673f4486e42..44e6b352d9a 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -2314,6 +2314,151 @@ cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call, } } +/* Variables and functions to manage constexpr call expansion context. + These do not need to be marked for PCH or GC. */ + +/* FIXME remember and print actual constant arguments. */ +static vec call_stack; +static int call_stack_tick; +static int last_cx_error_tick; + +/* 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) optionally with 16 ored in if it should use caller's caller location + instead of caller's location 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. The first message is a tag, with "" passed + for no tag, data () should return const char *, the tag should only contain + alphanumeric letters or underscores. The second message is the diagnostic + message, data () can be either const char * or const char8_t *. size () + should return the corresponding length of the strings in bytes as an + integer. */ + +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]) > 18 + || (wi::to_widest (args[0]) & 15) > 2) + { + if (!ctx->quiet) + error_at (loc, "first %qs call argument should be 0, 1, 2, 16, 17 or " + "18", "__builtin_constexpr_diag"); + *non_constant_p = true; + return t; + } + const char *msgs[2] = {}; + int lens[3] = {}; + cexpr_str cstrs[2]; + 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; + } + } + cstrs[i - 1].message = arg; + if (!cstrs[i - 1].type_check (loc, i == 2)) + { + *non_constant_p = true; + return t; + } + if (!cstrs[i - 1].extract (loc, msgs[i - 1], lens[i - 1], ctx, + non_constant_p, overflow_p, jump_target)) + { + if (*jump_target) + return NULL_TREE; + *non_constant_p = true; + return t; + } + } + 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; + return t; + } + } + if (ctx->manifestly_const_eval == mce_unknown) + { + *non_constant_p = true; + return t; + } + int arg0 = tree_to_uhwi (args[0]); + if (arg0 & 16) + { + arg0 &= 15; + if (!call_stack.is_empty ()) + { + tree call = call_stack.last (); + if (EXPR_HAS_LOCATION (call)) + loc = EXPR_LOCATION (call); + } + } + if (arg0 == 0) + kind = diagnostics::kind::note; + else if (arg0 == 1) + 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]); + return void_node; +} + /* 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 +2519,10 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, 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)) @@ -2846,14 +2995,6 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, return binds; } -/* Variables and functions to manage constexpr call expansion context. - These do not need to be marked for PCH or GC. */ - -/* FIXME remember and print actual constant arguments. */ -static vec call_stack; -static int call_stack_tick; -static int last_cx_error_tick; - static int push_cx_call_context (tree call) { diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index eb30d780d6c..0b096ccd727 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -986,6 +986,9 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) &CALL_EXPR_ARG (*expr_p, 0)); break; + case CP_BUILT_IN_CONSTEXPR_DIAG: + *expr_p = void_node; + break; default: break; } diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f205777e1af..cc34663b9c6 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7112,6 +7112,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 }; @@ -9549,12 +9550,15 @@ struct push_access_scope_guard class cexpr_str { public: + cexpr_str () : message (NULL_TREE) {} cexpr_str (tree message) : message (message) {} cexpr_str (const cexpr_str &) = delete; ~cexpr_str () { XDELETEVEC (buf); } - bool type_check (location_t location); - bool extract (location_t location, const char * & msg, int &len); + bool type_check (location_t location, bool allow_char8_t = false); + bool extract (location_t location, const char * &msg, int &len, + const constexpr_ctx * = NULL, bool * = NULL, + bool * = NULL, tree * = NULL); bool extract (location_t location, tree &str); tree message; private: diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 97460e448ae..6b210a30b6a 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -5636,6 +5636,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. */ diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index ba5c8fa1b55..7f2ea938370 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -12710,7 +12710,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; @@ -12746,7 +12746,7 @@ cexpr_str::type_check (location_t location) 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 % " @@ -12754,8 +12754,17 @@ cexpr_str::type_check (location_t location) "%"); 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 % " @@ -12785,95 +12794,229 @@ cexpr_str::extract (location_t location, tree &str) Returns true if successful, otherwise false. */ bool -cexpr_str::extract (location_t location, const char * & msg, int &len) +cexpr_str::extract (location_t location, const char * &msg, int &len, + const constexpr_ctx *ctx /* = NULL */, + bool *non_constant_p /* = NULL */, + bool *overflow_p /* = NULL */, + tree *jump_target /* = NULL */) { tsubst_flags_t complain = tf_warning_or_error; msg = NULL; if (message_sz && message_data) { - tree msz = cxx_constant_value (message_sz, NULL_TREE, complain); + tree msz; + if (ctx) + { + msz = cxx_eval_constant_expression (ctx, message_sz, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + if (*jump_target || *non_constant_p) + return false; + } + else + msz = cxx_constant_value (message_sz, NULL_TREE, complain); if (!tree_fits_uhwi_p (msz)) { - error_at (location, - "constexpr string % " - "must be a constant expression"); + if (!ctx || !cxx_constexpr_quiet_p (ctx)) + error_at (location, + "constexpr string % " + "must be a constant expression"); return false; } else if ((unsigned HOST_WIDE_INT) (int) tree_to_uhwi (msz) != tree_to_uhwi (msz)) { - error_at (location, - "constexpr string message % " - "%qE too large", msz); + if (!ctx || !cxx_constexpr_quiet_p (ctx)) + error_at (location, + "constexpr string message % " + "%qE too large", msz); return false; } len = tree_to_uhwi (msz); - tree data = maybe_constant_value (message_data, NULL_TREE, - mce_true); - if (!reduced_constant_expression_p (data)) - data = NULL_TREE; - if (len) - { - if (data) - msg = c_getstr (data); - if (msg == NULL) - buf = XNEWVEC (char, len); - for (int i = 0; i < len; ++i) - { - tree t = message_data; - if (i) - t = build2 (POINTER_PLUS_EXPR, - TREE_TYPE (message_data), message_data, - size_int (i)); - t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t); - tree t2 = cxx_constant_value (t, NULL_TREE, complain); - if (!tree_fits_shwi_p (t2)) + tree data; + if (ctx) + { + data = cxx_eval_constant_expression (ctx, message_data, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + if (*jump_target || *non_constant_p) + return false; + STRIP_NOPS (data); + if (TREE_CODE (data) != ADDR_EXPR) + { + unhandled: + if (!cxx_constexpr_quiet_p (ctx)) + error_at (location, "unhandled return from %"); + return false; + } + tree str = TREE_OPERAND (data, 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 || *non_constant_p) + return false; + if (TREE_CODE (str) == STRING_CST) + { + if (TREE_STRING_LENGTH (str) < len + || (unsigned) TREE_STRING_LENGTH (str) < off + || (unsigned) TREE_STRING_LENGTH (str) < off + len) + goto unhandled; + msg = TREE_STRING_POINTER (str) + off; + goto translate; + } + if (TREE_CODE (str) != CONSTRUCTOR + || TREE_CODE (TREE_TYPE (str)) != ARRAY_TYPE) + goto unhandled; + char *b; + if (len < 64) + b = XALLOCAVEC (char, len + 1); + else + { + buf = XNEWVEC (char, len + 1); + b = buf; + } + msg = b; + memset (b, 0, len + 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 + len) + b[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 + len) + b[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 + len) + b[l - off] = tree_to_shwi (value); + l++; + } + b[len] = '\0'; + } + else + { + data = maybe_constant_value (message_data, NULL_TREE, mce_true); + if (!reduced_constant_expression_p (data)) + data = NULL_TREE; + if (len) + { + if (data) + msg = c_getstr (data); + if (msg == NULL) + buf = XNEWVEC (char, len); + for (int i = 0; i < len; ++i) + { + tree t = message_data; + if (i) + t = build2 (POINTER_PLUS_EXPR, + TREE_TYPE (message_data), message_data, + size_int (i)); + t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t); + tree t2 = cxx_constant_value (t, NULL_TREE, complain); + if (!tree_fits_shwi_p (t2)) + { + error_at (location, + "constexpr string % " + "must be a constant expression", i); + return false; + } + if (msg == NULL) + buf[i] = tree_to_shwi (t2); + /* If c_getstr worked, just verify the first and + last characters using constant evaluation. */ + else if (len > 2 && i == 0) + i = len - 2; + } + if (msg == NULL) + msg = buf; + } + else if (!data) + { + /* We don't have any function to test whether some + expression is a core constant expression. So, instead + test whether (message.data (), 0) is a constant + expression. */ + data = build2 (COMPOUND_EXPR, integer_type_node, + message_data, integer_zero_node); + tree t = cxx_constant_value (data, NULL_TREE, complain); + if (!integer_zerop (t)) { error_at (location, - "constexpr string % " - "must be a constant expression", i); + "constexpr string % " + "must be a core constant expression"); return false; } - if (msg == NULL) - buf[i] = tree_to_shwi (t2); - /* If c_getstr worked, just verify the first and - last characters using constant evaluation. */ - else if (len > 2 && i == 0) - i = len - 2; - } - if (msg == NULL) - msg = buf; - } - else if (!data) - { - /* We don't have any function to test whether some - expression is a core constant expression. So, instead - test whether (message.data (), 0) is a constant - expression. */ - data = build2 (COMPOUND_EXPR, integer_type_node, - message_data, integer_zero_node); - tree t = cxx_constant_value (data, NULL_TREE, complain); - if (!integer_zerop (t)) - { - error_at (location, - "constexpr string % " - "must be a core constant expression"); - return false; } } + } + else + { + tree eltype = TREE_TYPE (TREE_TYPE (message)); + int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (eltype)); + msg = TREE_STRING_POINTER (message); + len = TREE_STRING_LENGTH (message) / sz - 1; + } +translate: + if ((message_sz && message_data) || ctx) + { /* Convert the string from execution charset to SOURCE_CHARSET. */ cpp_string istr, ostr; istr.len = len; istr.text = (const unsigned char *) msg; + enum cpp_ttype type = CPP_STRING; + if (message_sz && message_data) + { + if (POINTER_TYPE_P (TREE_TYPE (message_data)) + && (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (message_data))) + == char8_type_node)) + type = CPP_UTF8STRING; + } + else if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (message))) + == char8_type_node) + type = CPP_UTF8STRING; if (len == 0) ; - else if (!cpp_translate_string (parse_in, &istr, &ostr, CPP_STRING, + else if (!cpp_translate_string (parse_in, &istr, &ostr, type, true)) { - error_at (location, "could not convert constexpr string from " - "ordinary literal encoding to source character " - "set"); + if (type == CPP_UTF8STRING) + error_at (location, "could not convert constexpr string from " + "UTF-8 encoding to source character " + "set"); + else + error_at (location, "could not convert constexpr string from " + "ordinary literal encoding to source character " + "set"); return false; } else @@ -12884,13 +13027,6 @@ cexpr_str::extract (location_t location, const char * & msg, int &len) len = ostr.len; } } - else - { - tree eltype = TREE_TYPE (TREE_TYPE (message)); - int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT (eltype)); - msg = TREE_STRING_POINTER (message); - len = TREE_STRING_LENGTH (message) / sz - 1; - } return true; } diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 74e387c2d41..20288ed04eb 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -569,6 +569,7 @@ builtin_valid_in_constant_expr_p (const_tree decl) 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; diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag1.C b/gcc/testsuite/g++.dg/ext/constexpr-diag1.C new file mode 100644 index 00000000000..086784bff01 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/constexpr-diag1.C @@ -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" } +} diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag2.C b/gcc/testsuite/g++.dg/ext/constexpr-diag2.C new file mode 100644 index 00000000000..21f3f7350e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/constexpr-diag2.C @@ -0,0 +1,63 @@ +// { dg-do compile { target c++26 } } + +#include + +namespace std +{ +#if __has_builtin(__builtin_constexpr_diag) + struct _S_constexpr_tag_str { + private: + string_view _M_str; + public: + template + requires convertible_to + 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(16, "", __msg); } + constexpr void constexpr_print_str(u8string_view __msg) noexcept + { return __builtin_constexpr_diag(16, "", __msg); } + constexpr void constexpr_print_str(_S_constexpr_tag_str __tag, + string_view __msg) noexcept + { return __builtin_constexpr_diag(16, __tag._M_str, __msg); } + constexpr void constexpr_print_str(_S_constexpr_tag_str __tag, + u8string_view __msg) noexcept + { return __builtin_constexpr_diag(16, __tag._M_str, __msg); } + constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag, + string_view __msg) noexcept + { return __builtin_constexpr_diag(17, __tag._M_str, __msg); } + constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag, + u8string_view __msg) noexcept + { return __builtin_constexpr_diag(17, __tag._M_str, __msg); } + constexpr void constexpr_error_str(_S_constexpr_tag_str __tag, + string_view __msg) noexcept + { return __builtin_constexpr_diag(18, __tag._M_str, __msg); } + constexpr void constexpr_error_str(_S_constexpr_tag_str __tag, + u8string_view __msg) noexcept + { return __builtin_constexpr_diag(18, __tag._M_str, __msg); } +#endif +} + +consteval +{ + std::constexpr_print_str("foo"); // { dg-message "constexpr message: foo" } + std::constexpr_print_str(u8"bar"); // { dg-message "constexpr message: bar" } + std::constexpr_print_str("uninitialized", "foo"); // { dg-message "constexpr message: foo \\\[uninitialized\\\]" } + std::constexpr_print_str("uninitialized", u8"bar"); // { dg-message "constexpr message: bar \\\[uninitialized\\\]" } + std::constexpr_warning_str("uninitialized", "foo"); // { dg-warning "constexpr message: foo \\\[uninitialized\\\]" } + std::constexpr_warning_str("uninitialized", u8"bar"); // { dg-warning "constexpr message: bar \\\[uninitialized\\\]" } + std::constexpr_error_str("uninitialized", "foo"); // { dg-error "constexpr message: foo \\\[uninitialized\\\]" } + std::constexpr_error_str("uninitialized", u8"bar"); // { dg-error "constexpr message: bar \\\[uninitialized\\\]" } +} diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag3.C b/gcc/testsuite/g++.dg/ext/constexpr-diag3.C new file mode 100644 index 00000000000..4001b5c71ed --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/constexpr-diag3.C @@ -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, 2, 16, 17 or 18" } +consteval { __builtin_constexpr_diag (-42, "", ""); } // { dg-error "first '__builtin_constexpr_diag' call argument should be 0, 1, 2, 16, 17 or 18" } +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 '.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" } diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag4.C b/gcc/testsuite/g++.dg/ext/constexpr-diag4.C new file mode 100644 index 00000000000..6283b055923 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/constexpr-diag4.C @@ -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" } +} diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag5.C b/gcc/testsuite/g++.dg/ext/constexpr-diag5.C new file mode 100644 index 00000000000..924c7d3c7dc --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/constexpr-diag5.C @@ -0,0 +1,65 @@ +// { dg-do compile { target c++26 } } +// { dg-require-iconv "IBM1047" } +// { dg-additional-options "-fexec-charset=IBM1047" } + +#include + +namespace std +{ +#if __has_builtin(__builtin_constexpr_diag) + struct _S_constexpr_tag_str { + private: + string_view _M_str; + public: + template + requires convertible_to + 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(16, "", __msg); } + constexpr void constexpr_print_str(u8string_view __msg) noexcept + { return __builtin_constexpr_diag(16, "", __msg); } + constexpr void constexpr_print_str(_S_constexpr_tag_str __tag, + string_view __msg) noexcept + { return __builtin_constexpr_diag(16, __tag._M_str, __msg); } + constexpr void constexpr_print_str(_S_constexpr_tag_str __tag, + u8string_view __msg) noexcept + { return __builtin_constexpr_diag(16, __tag._M_str, __msg); } + constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag, + string_view __msg) noexcept + { return __builtin_constexpr_diag(17, __tag._M_str, __msg); } + constexpr void constexpr_warning_str(_S_constexpr_tag_str __tag, + u8string_view __msg) noexcept + { return __builtin_constexpr_diag(17, __tag._M_str, __msg); } + constexpr void constexpr_error_str(_S_constexpr_tag_str __tag, + string_view __msg) noexcept + { return __builtin_constexpr_diag(18, __tag._M_str, __msg); } + constexpr void constexpr_error_str(_S_constexpr_tag_str __tag, + u8string_view __msg) noexcept + { return __builtin_constexpr_diag(18, __tag._M_str, __msg); } +#endif +} + +consteval +{ + std::constexpr_print_str("foo"); // { dg-message "constexpr message: foo" } + std::constexpr_print_str(u8"bar"); // { dg-message "constexpr message: bar" } + std::constexpr_print_str("uninitialized", "foo"); // { dg-message "constexpr message: foo \\\[uninitialized\\\]" } + std::constexpr_print_str("uninitialized", u8"bar"); // { dg-message "constexpr message: bar \\\[uninitialized\\\]" } + std::constexpr_warning_str("uninitialized", "foo"); // { dg-warning "constexpr message: foo \\\[uninitialized\\\]" } + std::constexpr_warning_str("uninitialized", u8"bar"); // { dg-warning "constexpr message: bar \\\[uninitialized\\\]" } + std::constexpr_error_str("uninitialized", "foo"); // { dg-error "constexpr message: foo \\\[uninitialized\\\]" } + std::constexpr_error_str("uninitialized", u8"bar"); // { dg-error "constexpr message: bar \\\[uninitialized\\\]" } +} diff --git a/gcc/testsuite/g++.dg/ext/constexpr-diag6.C b/gcc/testsuite/g++.dg/ext/constexpr-diag6.C new file mode 100644 index 00000000000..fae3bd33573 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/constexpr-diag6.C @@ -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, 2, 16, 17 or 18" } +consteval { __builtin_constexpr_diag (-42, "", ""); } // { dg-error "first '__builtin_constexpr_diag' call argument should be 0, 1, 2, 16, 17 or 18" } +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 '.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" }